【cg】【球谐光照】PRT与PRTProbe
前言
书接前文【cg】【球谐光照】球谐函数。
上回书基本上讨论完了我们在实现本文所需要的理论知识,本文是对前面几篇理论知识的综合应用。
其中核心应用的是球谐函数的投影与重建
,以及球谐函数的正交完备性
和旋转不变性
。
本文需要用到的在上回书中提到的公式列举如下。
PRT
预计算光照传输(Precomputed Radiance Transfer)
[7]是指将渲染方程分为光照部分
和传输部分
,利用球谐函数的性质,分别预计算其球谐系数,以达到在运行时省略掉积分运算,快速计算出物体表面辐射照度的全局光照方法。
在PBR
[8]里我们了解过对于法线方向为
或

其中光照部分
,其余的便为传输部分
,记为
此时渲染方程可表示为
根据
光源部分
设其球谐系数为
其中球谐系数由
传输部分
设传输部分的球谐系数为
同理有其系数也需要做球面积分。
将
可以看到,上述推导利用了球谐函数的正交完备性
,即公式
需要注意的是,漫反射(diffuse)
的brdf是
而对于光泽反射(glossy)
和镜面反射(specular)
而言,其brdf与观察方向是有关的,不同的观察方向,求得的
在实际应用中,我们无法求出所有观察方向对应的离散化
而有限个数,只对这有限个方向计算球谐系数,然后再通过在这有限个结果中插值得到任意观察方向的球谐系数或最终辐射照度的近似值。
PRT Probe
在实际应用中,我们无法对场景中所有的物体表面都将它们的光照的系数和传输的系数求出来,这种计算量是相当大的。
我们通常会在场景中选择一些代表
,我们只计算这些代表
处的球谐系数和光照结果,这些代表
就是我们经常谈到的光照探针(light probe)
。

如图所示,当我们需要点a
处的光照结果时,我们会计算以probe
所在位置为位置,以点a
的法线方向为法线方向的假想表面
的光照结果。计算点b
的时候同理。
所以对于一个probe
来说,我们需要计算朝向四面八方的无数个法线位置对应的光照结果,这样才有能力代表
它周围朝向不同法线的物体表面。在实际应用中我们通常离散化出有限数量个法线方向进行计算,并通过法线方向进行插值得到任意法线方向的近似结果。
除此之外,由于一个probe
周围不同的物体表面都有不同的材质属性,如果我们在计算光照的过程中需要使用到这些属性,比如计算glossy
时需要粗糙度,比如计算specular
时需要粗糙度、金属度等,probe
无法决定使用哪位表面的材质属性进行计算,所以probe
一般很难计算glossy
和specular
,一般我们只计算diffuse
部分。
对于漫反射的光源部分的球谐系数(6.6),因为它是与法线方向无关的,所以对于任意法线方向来说,光源部分的球谐系数都是一样的,总共只需要计算一次即可。
对于漫反射的传输部分的球谐系数,我们将其brdf值
我们当然可以离散化有限个法线方向分别使用上述公式求出每个法线方向对应的系数,但是利用球谐函数的旋转不变性
,我们有一个更好的做法。
首先,我们将这个公式写回带
我们先只求法线方向为正z轴的球谐系数,即
由于目标函数与方位角
对于其他的任意法线方向旋转不变性
,可通过代入
将
这就是传输部分旋转后的球谐系数的表达式,其中勒让德多项式
,如果我们代入勒让德多项式
的表达式
比如
再比如
至此,对于Probe的任意法线方向的漫反射,其光照部分的球谐系数只需要算一次积分
ue4本身封装了传输部分的球谐系数的函数,如下所示,可以看到,与我们计算出来的结果是一致的。
1 |
|
实现相关
光源部分球谐系数
由蒙特卡洛积分
将该定积分转换为有限数量次的均值求出近似值。
当然也可以使用黎曼和
,以步进极角和方向角的方式求这个定积分的近似值。
所以关于每一个积分项的代码如下 ,其中Radiance
便是入射的光源computer shader
去计算这个累加结果。
1 | FThreeBandSHVector SHBasisCoefficients = SHBasisFunction3(TexelNormal); |
传输部分球谐系数
根据
1 | FThreeBandSHVector DiffuseTransferCofficients = CalcDiffuseTransferSH3(TexelNormal, 1) / PI; |
Irradiance
根据辐射照度
结果。
1 | float3 Irradiance = max(half3(0, 0, 0), DotSH(LightCoefficients, DiffuseTransferCofficients)); |
显然,对于一个probe来说,我们可以求得任意一个法线方向对应的irradiance
结果,但是如果我们要把所有的结果都存下来,那至少每个probe都需要一个cubemap,这显然是不现实的。我们这里分别尝试了两种方法来解决这个问题。
1.再次利用球谐函数将irradiance结果展开成球谐系数并只保存球谐系数,在采样时利用系数重建irradiance值。
该方法需要使用
该方法存储的是球谐系数,所以在场景绘制采样时需要采样更多次,如三阶球谐至少需要采样3次才可将9个系数采样出来。
2.只求有限个法线方向的irradiance结果,其它法线方向通过已知的法线插值出最终结果。
这里选取直角坐标系坐标轴上的6个方向(+x、-x、+y、-y、+z、-z)进行存储,其他法线方向的结果由这些方向插值而来。
我们可以对法线方向进行正八面体编码
,以利用2D贴图的插值算法自动插值出结果,且只需要1次采样。

当然我们这里只个有6个顶点的值,所以最终一个probe存储的结果如下图所示。
我们将最终所有probe的结果按照ID保存在一张贴图上,最终生成的整个场景的irradiance结果如下所示。
