URP 渲染实战 / 第 11 章 从零手写 PBR / 案例 11.7
案例 11.7多次散射能量补偿高阶
目标:让粗糙金属不再“越粗糙越暗”,并用白炉测试验证。
Cook-Torrance 模型只考虑了光在微表面上反射一次就离开的情况。被 判为“被挡住”的光,在现实中并没有消失,而是撞到相邻的微平面上再次反射,其中相当一部分最终也离开了表面。单次散射模型把这部分能量直接丢掉了,表面越粗糙、遮挡越多,丢得越多。
Kulla 与 Conty(SIGGRAPH 2017)给出了完整的补偿方法:用一个额外的 BRDF 项 把丢失的 加回来,其中 是单次散射的方向反照率。实时渲染里常用它的一个廉价变体(Fdez-Agüero 2019,Filament 采用):注意到 F0 = 1 时环境 BRDF 恰好就是 ,DFG 表里已经有了,于是把整个镜面项放大:
对白色金属(F0 = 1),放大系数是 ,环境镜面恰好补回到 1;对电介质(F0 = 0.04)放大量很小,因为电介质的镜面本来就弱,丢失的能量大部分被菲涅尔“吸收”了。代码里这个系数在 MyComputeLighting 开头算一次,存进 m.energyCompensation,直接光和环境光的镜面都乘上它。_EnergyCompensation 滑条在 0 和 1 之间插值,方便对比。

白炉测试是检验 BRDF 能量守恒的标准方法:在一个各方向亮度相同的环境里,一个不吸收任何能量的材质应该与背景无法区分。左图上排从左数第二个球开始就能看到轮廓,越往右越暗,这就是单次散射丢失的能量;右图补偿后上排完全隐形。下排的现象同样值得注意:彩色金属多次反弹时每次都乘一次 F0,颜色会越来越饱和——这正是 Kulla-Conty 指出的“粗糙彩色金属应该更饱和”,补偿项里的 F0 因子模拟了这一点。
注意在 IBL 上,补偿系数与 DFG 表来自同一个 ,白球“完全消失”在数学上是必然的,因此白炉测试在这里检验的是“补偿实现有没有写错”(例如乘错位置、漏乘 F0)。真正的物理正确性取决于 DFG 表本身是否准确,这一点由 LUT 的生成代码保证。