做过离线烘焙的人都遇到过这个场景:灯光打好了,材质调好了,点下烘焙按钮等了半小时,结果出来的光照贴图上密密麻麻全是斑点,近看像撒了一层芝麻,远看像信号不良的电视屏幕。这种噪点问题在间接光照区域尤其严重,因为间接光的贡献来自四面八方,采样不足时方差极大。本文围绕采样数与去噪两条主线,系统讲清楚斑点是怎么来的、怎么在源头减少它、以及怎么在烘焙之后把它抹掉。

斑点到底是怎么产生的
现代烘焙引擎(Unity的Progressive Lightmapper、Unreal的Swarm、Blender的Cycles)本质上都在做同一件事:用蒙特卡洛方法对场景的光传输进行积分估计。每张texel需要向半球方向发射若干条采样光线,统计它们命中光源或被其他表面反弹回来的辐照度,最后求平均得到该点的光照值。
问题在于这个估计是有方差的。当一条光线恰好命中面光源,返回值可能很高;当它落在阴影区域,返回值接近零。如果每个texel只发射几十条光线,随机波动就会在相邻texel之间产生明显差异,反映到贴图上就是明暗不一的斑点。理解了这一点就能明白为什么斑点在三种情况下格外严重:面光源越大、采样数越少时越明显;间接反弹次数越多越明显,因为每一跳都会累积新的方差;GI分辨率越低越明显,因为每个texel覆盖的世界空间更大,细节丢失后噪点对比度反而更高。
还要注意一个常见误区:斑点不等于bug。很多人看到噪点就怀疑引擎出问题,实际上是积分收敛不足的正常表现。真正要做的不是绕过它,而是从方差和收敛两个方向同时下手。
提高采样数:最直接但有代价的方案
既然噪点来自方差,最直觉的解法就是增加样本数量。根据蒙特卡洛积分的收敛特性,噪点方差与采样数成反比,也就是说想把噪点降低一半,采样数要翻倍;想降到十分之一,采样数要乘以十。这个残酷的收敛速度意味着单纯堆采样数很快会撞上烘焙时间的墙。
以Unity为例,在Lighting窗口的Baked Global Illumination设置中有几个关键参数:Samples控制每个texel的总采样数,Bounces控制间接反弹次数,Filtering控制后处理。一个实用的调参顺序是先固定Bounces为2到3,把Samples从默认的64逐步提到256、512,观察斑点消退的速度。如果从256提到512肉眼改善已经很小,说明瓶颈可能不在采样数上,而应该去检查光源面积和贴图分辨率。
// Unity中通过脚本设置烘焙采样参数 var settings = Lightmapping.bakedGI; // 每个texel发射的采样光线数量 Lightmapping.SetupSceneUV; // 实际操作中在Project Settings > Quality 中调整: // - Baked GI Samples: 512 // - Bounces: 2 // - Filtering: Gaussian (半径1px) + Denoising
另一个常被忽略的参数是Clamp Indirect。间接光中偶尔会出现极端亮值(比如光线恰好多次命中高反射面),这些离群样本会把噪点放大成刺眼的亮点。把Clamp Indirect设置在1到5之间可以有效截断离群值,代价是极亮区域会被压平。这个参数几乎不损失烘焙时间,属于性价比最高的单项调整。
过滤与去噪:烘焙后的救命稻草
当采样数的边际收益下降时,就该让后处理登场了。最基础的手段是高斯过滤,也就是对贴图做模糊。它对消除高频噪点很有效,但会连带把光照细节一起抹掉,角落的接触阴影、柱子背后的软阴影边缘都可能糊掉。半径设1到2像素通常比较安全,超过3像素细节损失就会很明显。
更现代的做法是使用基于人工智能的降噪器。Unity 2020之后的Progressive Lightmapper内置了NVIDIA OptiX降噪和AMD降噪(Radeon Pro denoiser),它们能区分噪点和真实的光照结构,在保留阴影边界的同时消除斑点。开启方式很简单,在Filtering里选择Denoising,或者组合模式Gaussian然后Denoising,先做轻度高斯模糊再用AI降噪收尾。这套组合是目前公认的效果与稳定性最平衡的配置。
// Unity Project Settings 中的推荐过滤配置示例 // Filtering 模式选择: Advanced // - Direct: Gaussian, radius 0.5 // - Indirect: Gaussian, radius 0.5 + Denoiser // - Ambient Occlusion: Gaussian, radius 1.0 // 配合 Samples = 256, Bounces = 2, Clamp Indirect = 2
如果引擎没有内置降噪器,也可以把烘焙结果导出到外部处理。常见流程是将光照贴图导出为EXR或PNG序列,用Blender的Compositor做双边缘滤波,或者借助OptiX的独立降噪工具、Intel Open Image Denoise(OIDN)批处理。OIDN是开源的,支持命令行批处理大量贴图,对中小团队非常友好,处理一张2K贴图通常只需要几秒。
光源与场景层面的治本手段
采样和去噪都是治标,斑点的根源有时出在场景设置上。第一个要检查的是光源面积。面积越大的面光源越容易产生斑点,因为采样光线命中与否的随机性更强。如果一个大面光主要是为了营造柔和的环境光,考虑改用环境探针或HDRI环境光来承担这部分职责,把面光源留给真正需要形状感的区域,噪点问题会立刻缓解。
第二个是材质的反射率设置。理论上真实世界的漫反射率很少超过0.8,但很多新手会把材质颜色直接调成纯白或者高饱和亮色,导致间接光能量在场景里来回反弹放大,方差随之飙升。把贴图过亮的部分压到0.8以下,配合Clamp Indirect,能显著改善间接光照区域的干净程度。
第三个是光照贴图分辨率与texel密度的匹配。在Unity中查看Lightmap的Texels per Unit,经验值是场景主体区域10到20之间。密度太低时,即使采样充足,插值产生的块状感也会被误认为噪点;密度太高则浪费内存和烘焙时间。可以借助Lighting窗口的Baked Lightmap预览里的方向性与强度视图,精确定位哪些区域需要单独调分辨率,结合Scale In Lightmap参数对单个物体做差异化设置,把采样预算花在玩家会凑近看的地方。
推荐的参数搭配与验证流程
综合上面的分析,给出一套可以直接套用的基线配置:GI分辨率按场景尺度设置在10到20 texels per unit,Samples设在256到512之间,Bounces为2,Clamp Indirect为2,Filtering采用Gaussian半径0.5加AI降噪的组合。这套配置在多数中型场景下能在半小时左右的烘焙时间内得到干净的光照贴图。
最后强调验证流程的重要性。调试斑点问题时不要每次都做完整烘焙,Unity的Progressive Lightmapper支持实时预览,可以在烘焙进行到几个百分点时就观察噪点趋势;更好的做法是选定一个典型的小区域,把摄像机对准它做局部快速迭代,参数确认后再全场景烘焙。同时建议保留一组参数对照记录,比如在项目管理文档里记下每次调整的Samples、Filtering和对应的烘焙时长,长期积累下来你会对自己的项目在质量与时间的曲线上处于哪个位置有清晰判断,避免每次都从零开始摸索。