光线追踪带来了前所未有的真实感渲染效果,柔和的阴影、准确的全局光照、真实的景深和运动模糊,这些在传统光栅化管线中难以实现的效果,在路径追踪器中都是天然支持的。但代价也很明显:如果不做任何处理,直接渲染出来的图像往往布满噪点,尤其是暗部区域和间接光照部分,看起来像老式电视的雪花屏。这篇文章就来深入分析噪点到底是怎么产生的,以及如何通过采样策略和降噪技术来系统性地解决它。

噪点从哪里来:蒙特卡洛积分的方差问题
路径追踪的本质是用蒙特卡洛方法求解渲染方程。渲染方程描述了场景中一点沿某方向发出的辐射亮度,它是一个包含半球积分的复杂方程,无法解析求解,于是渲染器随机发射光线采样,用有限次数的随机采样平均值来近似积分结果。
问题就出在“随机”和“有限”这两个词上。根据大数定律,采样次数越多,平均值越接近真实积分值,误差以O(1/√N)的速度收敛。注意这个开根号关系:采样数提升4倍,噪点只减少一半;想要噪点减少到原来的十分之一,采样数需要提升100倍。这就是为什么单纯堆采样数是一条极其昂贵的路。
具体到渲染画面中,方差的来源主要有几类。第一类是相机光线击中光滑表面时的镜面反射方向随机化,粗糙度越高采样越难收敛;第二类是面积光源的柔和阴影,随机采样光源表面导致阴影边缘闪烁;第三类是路径追踪中的Russian Roulette路径终止策略,路径长度随机截断引入额外方差;第四类是参与介质(雾、体积光)中的散射事件采样。理解这些来源,才能对症下药。
采样策略优化:让每一根光线都物有所值
既然盲目增加采样数不划算,那就必须提高每次采样的“质量”。这就是采样策略优化的核心思想:让光线尽量打到对结果贡献大的地方。
分层采样与蓝噪声抖动
纯随机采样容易产生采样点聚集和空缺,分层采样(Stratified Sampling)把采样域划分成若干格子,每个格子内只采样一次,能显著降低方差。蓝噪声抖动(Blue Noise Dithering)则更进一步,它把像素间的残差用蓝噪声序列分散开,让噪点分布更均匀、更符合人眼感知。人眼对低频噪点更敏感,把噪点能量推向高频段,即使方差相同,视觉观感也会好很多。Blender Cycles从3.x版本开始引入的scrambling distance和渐进式蓝噪声抖动,就是这个思路的产物。
重要性采样与多重重要性采样
重要性采样按照被积函数的形状来构造采样分布,光线更倾向于打到能量贡献大的方向。最典型的例子是对光源采样:与其随机地按BSDF采样然后祈祷能命中光源,不如直接在光源表面上采样,然后用多重重要性采样(MIS)在BSDF采样和光源采样之间加权平衡。下面的伪代码展示了MIS的权重计算思路:
// 多重重要性采样:合并BSDF采样与光源采样的估计
Spectrum estimate = 0.0;
// 1. 按BSDF采样一个方向
Vector dir = sampleBSDF(hitPoint, normal);
if (rayHitsLight(dir)) {
float wBsdf = misWeight(bsdfPdf(dir), lightPdf(dir));
estimate += wBsdf * bsdfEval(dir) * lightEmission / bsdfPdf(dir);
}
// 2. 在光源表面直接采样
LightSample ls = sampleLight();
if (rayVisible(hitPoint, ls.point)) {
Vector dirToLight = normalize(ls.point - hitPoint);
float wLight = misWeight(ls.pdf, bsdfPdf(dirToLight));
estimate += wLight * bsdfEval(dirToLight) * ls.emission / ls.pdf;
}
return estimate;
MIS的威力在于它几乎消除了BSDF采样在小光源上的高方差,也消除了光源采样在镜面表面上的低效率。几乎所有现代路径追踪器(Cycles、Mitsuba、PBRT)都把它作为标配。
降噪器:用算法或神经网络抹掉残余噪点
即使采样策略已经优化到位,要达到完全干净的画面可能还需要上千个采样点,渲染时间难以接受。降噪器的思路是:用较低的采样数快速渲染出一张带噪点的图,然后借助辅助信息(法线、深度、反照率、运动矢量)把噪点滤除,同时尽量保留结构和细节。这本质上是拿少量时间换大量采样,性价比极高。
OptiX AI降噪器
NVIDIA的OptiX Denoiser基于卷积神经网络训练,运行在GPU上,速度极快,4K图像通常几百毫秒就能处理完。它在Blender Cycles中通过设置Denoising为OptiX即可启用,需要RTX系列显卡。它的优势是对HDR色彩空间的处理比较自然,缺点是偶发细节涂抹,比如细腻的纹理颗粒可能被抹掉。
Intel Open Image Denoise(OIDN)
OIDN是Intel开源的CPU降噪方案,质量与OptiX接近,不依赖NVIDIA显卡,兼容性更好。它支持albedo preset和normal auxiliary,其中反照率引导(albedo guiding)非常关键:噪点图中物体的固有色本身带有噪声,如果把带噪的albedo喂给降噪器,它会把漫反射噪点误认为纹理。正确做法是单独渲染一张无光照的albedo通道作为引导图。在Cycles的节点系统中可以这样连接:
<!-- Cycles中启用OIDN降噪时的典型渲染设置(Python脚本形式) --> import bpy scene = bpy.context.scene # 启用OIDN降噪并使用精确的albedo和normal引导 scene.cycles.use_denoising = True scene.cycles.denoiser = 'OPENIMAGEDENOISE' scene.cycles.denoising_use_gpu = True # 使用GPU后端 scene.cycles.denoising_prefilter = 'ACCURATE' # 精确预过滤引导通道
注意最后一项prefilter设置为ACCURATE,这是处理带噪引导图的关键选项,如果albedo通道本身噪点严重,Accurate模式会先做预滤波再送入网络。
时域降噪:动画场景的必修课
静态图降噪相对简单,动画就麻烦得多。如果每一帧独立降噪,残差会在帧与帧之间随机变化,画面出现闪烁,人眼对闪烁极其敏感。解决办法是时域累积:利用运动矢量把当前帧的采样累积到上一帧的像素上,等效采样数随帧数增长。实时渲染中的Temporal Anti-Aliasing以及DLSS的时域超分都基于此。但时域累积会引入鬼影——快速移动的物体在旧位置留下残影,需要通过夹紧颜色范围(clamping)和失效检测来缓解。游戏引擎和实时路径追踪(如Cyberpunk 2077的RT Overdrive模式)在这方面的工程细节值得深入研究。
实战建议:组合拳才是正解
解决了原理层面的问题,最后聊聊实际的工程流程。噪点治理从来不是单一手段能完成的,采样优化和降噪器的组合才是标准答案。
第一个建议是先定位噪点来源再对症下药。渲染一张低采样图观察噪点分布:如果噪点集中在阴影边缘,优先检查光源采样和MIS是否启用;如果间接反弹的暗部噪点严重,考虑增加反弹次数的预算或使用光照缓存类的技术;如果整个画面均匀布满噪点,那是采样数本身不足,提高每像素采样数是最直接的。
第二个建议是给降噪器“合适的输入”。大量实践表明,128到256个采样加高质量降噪的效果往往优于1024采样无降噪,而前者可能只需要后者十分之一的时间。但采样数也不能太低,64采样以下时,降噪器面对严重的信息缺失容易产生色斑和细节幻觉,尤其是玻璃、焦散这类难以重建的像素。
第三个建议是注意HDR数值的处理。降噪应在线性HDR空间进行,而不是先转成8位LDR再降噪,后者会丢失亮部信息并在高光区域产生奇怪的色偏。焦散和镜面高光这类镜面路径的噪点,AI降噪器的表现普遍不佳,更稳妥的方案是单独渲染焦散通道或使用Manifold Next Event Estimation这类专门的焦散采样技术。
总结一下,噪点治理的完整链条是:用重要性采样和MIS降低方差,用分层采样和蓝噪声改善噪点分布,用适量采样数提供足够信息,最后用AI降噪器收尾。理解每个环节的原理,你就能在质量与时间之间做出最适合自己的取舍。