摘要:Split Sum通过将环境光照积分拆分为BRDF滤波和光照滤波两部分,利用预计算降维技术加速实时渲染。BRDF部分采用菲涅尔近似降维,光照部分使用多级mipmap快速查询,显著提升PBS的IBL计算效率。
基于图像的光照(Image based lighting, IBL)是一类光照技术的集合.其光源不是如点光源等直接光源,而是将周围环境整体视为一个大光源.
那么在shader中我们有了一张cubeMap(也可能是别的形式),如何用它来实现PBS(Physically Based Shading)呢?
或者说,我们如何用一张环境贴图来解渲染方程呢?
首先回顾渲染方程:
L(p,ωo)=∫H2f(p,ωi⋅ωo)Li(p,ωi)V(p,wi)cosθidwi
在环境光照中,我们不考虑Visibility即不考虑阴影(任何方向光照都能到达),所以可以去除这一项:
L(p,ωo)=∫H2f(p,ωi⋅ωo)Li(p,ωi)cosθidwi
我们可以用蒙特卡洛方法来积分:
L(p,ωo)≈N1i=1∑Nf(p,ωi⋅ωo)Li(p,ωi)cosθi
但是问题是蒙特卡洛积分需要大量的采样,如果在shader里用的话,速度肯定会十分感人.
那么如何去避免采样来计算呢?
人们观察到一件事情:
- 如果BRDF比较glossy,虽然值变化大,但是"积分域"小
- 如果BRDF比较diffuse,虽然"积分域"大,但是值变化小.
如果读者阅读过这篇文章,马上就会想起这个约等式:
∫Ωf(x)g(x)dx≈∫ΩGdx∫ΩGf(x)dx∫Ωg(x)dx
注意这里有一点小区别:f(x)的积分域变成了整个范围中g(x)有值的地方.
而这个约等式在以下情况比较准确:
那BRDF不是正好很满足这个性质吗?
那么我们可以更改我们的渲染方程:
L(p,ωo)≈∫Ωfrdwi∫ΩfrLi(p,ωi)dwi∫Ωfr(p,ωi⋅ωo)cosθidwi
相当于把BRDF的lobe上的Li积分起来然后normalize.
不就是filting吗?而filter就是lobe的大小.

更详细地说,看下图:

左边是正确的做法:在lobe里采样多次没有滤波的环境贴图.
就相当于采样一次镜面反射方向已经根据lobe大小进行滤波的环境贴图.
那么我们只要存储多张不同滤波的环境贴图,然后shading根据lobe大小选择对应的贴图进行三线性插值,就可以快速计算∫Ωfrdwi∫ΩfrLi(p,ωi)dwi
但是∫Ωfr(p,ωi⋅ωo)cosθidwi要如何进行积分呢?
我们可以想到使用预计算的方法,把所有可能的情况计算出来并且存储起来,但是问题也显然:
我们需要一个五维的表格来存储,这显然会占用太大内存.
那么如何降低这个维度呢?
如果回看Cook-Torrance BRDF,事实上我们只需要3个参数:
- 入射角θi
- F0
- roughness
三维似乎已经可以接受了,但是大佬又搞出来骚方法把三维降成了2维.怎么做到的呢?
首先起源于一个对菲涅尔项(使用Schlick近似)的发现:
如果我们将菲涅尔项从BRDF中提取出来可以得到:
∫Ω+fr(p,ωi,ωo)cosθidωi≈F0∫Ω+Ffr(1−(1−cosθi)5)cosθidωi+∫Ω+Ffr(1−cosθi)5cosθidωi
这样我们可以将F0从积分中提取出来,那么积分部分就只依赖两个变量(roughness和ωi),从而达到降维了.
这样我们可以预计算一张纹理两通道的纹理,r通道记录第一个积分,g通道记录第二个积分,这样计算时只需要查询两次就可以解决了.

这样,通过两种不同处理,我们就完美解决了环境光照积分的问题,而且结果和效率都十分喜人.这种方法被称为Split Sum.

至于为什么不叫Split Integral,是因为实时渲染中一般将公式写为求和的形式而非积分的形式,即:
N1k=1∑Np(lk,v)Li(lk)f(lk,v)cosθlk≈(N1k=1∑NLi(lk))(N1k=1∑Np(lk,v)f(lk,v)cosθlk)
那么只剩下一个问题了:有遮挡时应该怎么做呢,即怎么计算Visibility?
事实上,这件事情非常困难,到2024年为止还没有很好的解决方法.
但是有一个凑合的办法:从环境贴图中找一个最亮的光源生成shadow