转载请注明出处为KlayGE游戏引擎,本文的永久链接为http://www.klayge.org/?p=3050

上一篇把BRDF换成了更为常见的blinn-phong,推出了在上面进行importance sampling的公式,以及如何把结果存到一张查找表LUT上。更进一步的做法是把这张LUT拟合成一个曲面,这样就可以在shader中直接计算,省去一次纹理采样。Black Ops II也做了拟合,但它的方法是把F=0和F=1的曲线用F=0.04来表达,最终用一个很粗糙的插值来得到整个曲面。这里我打算用暴力的方法直接拟合整个曲面。

曲线拟合

LUT有两个通道,x表达scale,y表达bias。对于这两个通道,可以表达为f(n_dot_v, roughness)这样的一个函数。原始的LUT大小为128×128,实验中发现LUT本身很平滑,即使缩小到16×16,也不容易从最终渲染结果上看区别。所以这里我们选择16×16的大小作为拟合的数据源。这里对两个通道的处理过程是一样的,所以下面就拿x通道为例。

x通道的16×16数据在图像上看起来是这样的

LUT 16x16

我没有MatLab和Mathematica,只能用Excel来完成这个曲面拟合。(这里有个关于如何使用excel做曲线拟合的教程。)这里我用的方法是,对于每个roughness,先拟合出一条曲线。测试结果是,必须要用3次曲线才能保证误差较小。所以这里选定的曲线形式为

${A} \times {NoV}^3 + {B} \times {NoV}^2 + {C} \times {NoV} + {D}$

扩展到曲面

所有的16个roughness,都得到了一组(A, B, C, D)。这16组(A, B, C, D)的曲线是是这样的

Roughness 16

如果要真正贴合这个曲线,需要用到高达6次的方程。这样的计算量负担太大了。所以我在实际中也用了3次方程来拟合。

${E} \times {Roughness}^3 + {F} \times {Roughness}^2 + {G} \times {Roughness} + {H}$

现在只要对于ABCD四条曲线,都得到他们分别的(E, F, G, H)组合,就能把这四条曲线都表达出来了。下一步就是把roughness曲线带入原方程,得到整个曲面的描述

$({p1} \times {Roughness}^3+{p2} \times {Roughness}^2 + {p3} \times {Roughness}+{p4})\times {NoV}^3$
$+({p5} \times {Roughness}^3+{p6} \times {Roughness}^2+{p7} \times {Roughness}+{p8})\times {NoV}^2$
$+({p9} \times {Roughness}^3+{p10} \times {Roughness}^2+{p11} \times {Roughness}+{p12})\times {NoV}$
$+({p13} \times {Roughness}^3+{p14} \times {Roughness}^2+{p15} \times {Roughness}+{p16})$

其中p1到p16分别是四条曲线的(E, F, G, H)。

提高精度

需要注意的是,excel里显示的曲线参数精度不够,直接使用的话误差较大。在excel里,需要用solver功能来一个非线性优化,以最小化误差。这里用的误差描述是

$min(\sum (\frac{dest – src}{src})^2)$

其中dest是用拟合的公式算出来的结果,src是LUT的原数据。最终得到的的参数是

{-4.996914762, 8.993475061, -4.928234727, 0.846124834, 7.253111161, -15.42473953, 10.95286522, -1.596815751, -1.963867075, 4.712593001, -3.852980973, 1.005729748, -0.170416225, 0.448102365, -0.389332014, 0.113484128}

用这组参数,虽然直接算误差的话还是不小,但得到的结果和用LUT在视觉上除了最低的roughness有细微差别外,其他的都看不出区别。由于这些计算都能被采样prefiltered cubemap的IO所掩盖,所以速度上比使用LUT稍微快一点点。

Env BRDF Blinn-Phong Physically-based Fitting Prefiltered

总结

至此,KlayGE中基于物理的specular环境BRDF已经到了最终形态,只需要一次采样就能完成渲染。但这个曲面拟合的计算量还是有点大。未来如果找到更好的曲面形式,还可以拟合一个更简单的。下一篇会讲一下diffuse的环境BRDF。