找回密码
 立即注册
查看: 641|回复: 1

【Unreal从0到1】【第一章:物理的全局光照实现】1.1,PRT:预计算的全局光照

[复制链接]
发表于 2021-8-13 17:26 | 显示全部楼层 |阅读模式
任何全局光照方案的核心目的都是找到一种更好的方式去表述与求解光照传输方程,由此产生的渲染方程数学变种已经多达十余种。对于PRT来说,主要的手段有两种,一种是IBL,一种为球谐光照。实时渲染的工程应用上会配合着使用,即使用SH计算低频环境光,使用IBL计算环境反射光。
       关于SH所提供的环境漫射是否为有向的,也有不同看法,从球谐函数的几何图像来看确实波瓣在不同方向上是有区别的,但在相当一部分情况下会选择忽略掉这种方向性,因为无方向的光照分布确实有助于物理建模与数学建模,在本文中我们暂且忽略掉球谐的方向性。
       为了保证文章的核心话题,默认读者看完了《全局光照技术》[1]第1,4,10章与《LearnOpenGL》[2]
一,UE4的PRT GI策略
      UE4的PRT GI策略大概可以概括为如下:


       一些引擎在设计PBR时都会考虑将直接光照【无二次反弹光照】分为漫反射与高光反射两部分,但是UE4少有的又加了一个Transmission项,并且基于9类BxDF封装了13个ShadingModel作为直接光照的计算,具体会在第三章:Disney原则的PBR中重点分析UE4的PBL,PBS与PBR。这里主要讨论环境光计算部分,毕竟对于任何一种GI方案,能凸显其特质的都是比较难解决的EnvLighting。
二,球谐光照
2.1,为何是球谐
       对于直接光照来说,光源数量是有限的,因此我们可以直接求解渲染方程得到材料表面某一点在特定视角下的反射光【取决于BXDF】,当光线发生二次反弹时,携带材料颜色信息的反射光线会随着反弹次数增加呈几何增加,物理的模拟这一过程的方案被称为光线追踪。这对于实时渲染来说这种规模的计算量是不可接受的。
       对于所有二次反射光线来说,场景中物体的每一个反射点都可以考虑成一个新的光源,当把整个场景展开成一张图像时,就可以认为这张环境图是一组光源的集合,即将周围环境拍出一张HDR图,然后将HDR图转换为环境立方体贴图(Cubemap),再把Cubemap的每一个像素视为一个光源,然后对渲染方程进行半球积分,基于这种思想诞生了IBL技术。这种思想与数学上的二维卷积很像,所以可以对渲染方程进行卷积运算求得环境照明。


       这个思路看起来是很巧妙的,但实现起来有很多的细节需要进一步考虑。首先为了计算卷积要把渲染方程拆分为漫射积分与高光反射积分:


      对于漫射积分,进行物理建模时可以认为各个方向的环境漫射分布是相同的,适合直接卷积。


       将积分式离散化,然后求其黎曼和。卷积的结果是由Enviroment map得到一张Irradiance map(辐照图),自己测试的话可以使用CubemapGen工具[3]


       卷积的计算量是比较大的,对于静态场景没什么问题,可以离线生成辐照图,但纯静态场景的情况是比较少的,那么有没有一种更为讨巧的数学手段去降低渲染方程计算的复杂度呢?
       按照渲染方程的定义,光线在物体表面某一点的反射率等于定义域内所有方向入射辐照度乘上一个加权系数fr后的和,而按照普适的傅里叶定律,一个连续函数可以在其函数空间内分解为若干基函数与其对应加权系数的和。依据这一思想,可以把渲染方程的积分运算过程转换为若干基函数与其对应系数的和。
       一维空间的傅里叶展开其基函数使用的是余弦函数,对于球面积分f(x)来说理论上也可以找到一组基函数B_i(x)与对应系数C_i。这样只要找到C与B的具体表达式,就能够通过傅里叶展开的思想将难以计算的f(x)展开降低计算复杂性。
      现在假设球面函数为f(x),其基函数为B_i(x),对应的系数为c_i,依据数学原理c_i等于原函数f(x)在其基函数的对偶函数上的投影,也就是求解原函数与其基函数对偶函数卷积和的过程:


      如果有:


      则有:


      其中\tilde{B_i}(x)是B_i(x)的对偶函数,也就是说\tilde{B_i}(x)是B_i(x)要满足以下条件


       这样针对每一个基函数都有一个对应的系数c,有了系数c原函数便可以按照系数与基函数的对偶函数乘机和的方式重建出来,并且随着基函数取得越来越高阶,计算结果与原函数的拟合程度也就会越好


       因此基函数的选择很重要,因为一些基函数的特殊数学性质,使得它们能更为巧妙的达到简化计算的目的,比如正交基函数,其具备两个优良的性质
    (1)正交性
      对于两个不同的基函数B(i)与B(j),当i≠j时,这两个基函数的乘积积分为0;
    (2)规范性
      基函数B(i)与其自身乘积的积分结果为1
       也就是说正交基函数的对偶函数就是其本身,这样重建原函数时就不需要再根据基函数求得其对偶函数,另外规范化的基函数计算起来非常高效,原因如下:
       现假定有两个原函数f_a(x)与f_b(x),它们通过各自的基函数B_i(x)与B_j(x)及系数c_ai(x)与c_bj(x)表示,那么两者函数的乘积积分就可以表示为:


       因为规范化正交的基函数,当给定两个不同的基函数B_i(x)与B_j(x))且i≠j时,这两个基函数乘积的积分为0,所以上式大部分情况下为0,只有当i=j时才会参与有效的加和,而规范化的正交积自生乘机的积分值为1,所以上式就可以简化为:


       这样原本复杂的函数乘积的积分运算过程就转变为了其系数点积求和的过程,计算量减少了很多。所以基于该思路,可以将渲染方程的积分求解拆分为光照传输方程入射光辐照度函数乘积的积分求解,这样如果能找到一组规范化的正交基函数,原来难以计算的积分方程就会转变为简单系数点积的求和,而有一种基函数刚好能满足这些条件,它就是球形谐波函数
2.2,球谐函数推导
       简单分析一下球谐函数是怎么来的,一步步去推导可能需要1天时间。。。还得是数学功底好的【也很有可能是我太菜了。。。】,所以只是说明下关键步骤方便弄清楚球谐的阶与带是如何影响光照的
       球谐函数的数学本质其实就是拉普拉斯方程在球坐标系下的角度解,其求解过程先将拉普拉斯方程通过微分形式不变定理转换到球坐标下,然后使用分离变量法得到多个常微分方程,再利用伴随勒让德公式进行求解。
2.2.1,球坐标系下的拉普拉斯方程


       从公式可以看出拉普拉斯方程式一个二阶的偏微分方程,但这是基于直角坐标系的,需要将其转换到球坐标系下,转换方法如下:
      根据:


      可以得出:


      所以r,θ,φ是关于x,y,z的函数,如下:


      然后需要用到《高等数学》中非常重要的一个定理——微分形式不变定理:


      用这个公式处理拉普拉斯方程,消除x,y,z就得到了球坐标系下的拉普拉斯方程【这一步推导3个小时~~】:



2.2.2,拉普拉斯方程求解
       拉普拉斯方程的求解用到了《高等数学》中的“分离变量”的思想,分离变量法就是将一个偏微分方程分解为多个只含一个变量的常微分方程,将方程中各个变量的项分离开来从而将原始方程分解为多个更简单的只含一个自变量的常微分方程。定义复读完毕,也就知道为什么要用分离变量法求解拉普拉斯方程了。根据这一思想,设定:


       其中Y(θ,φ)代表角度部分的解,也就是球谐函数,R(r)则是径向函数。将公式(7)代入拉普拉斯方程,就可以得到下式:


        接下来用分离变量法对R进行分离变量,等式两边同时乘以(r^2/θΘ)/R,就可以得到


        其中的如下项与r变量无关:


        所以为了看起来更直观我们令:


        就有:


       按照这个思路,对Θ与Φ分离变量,可得到:


       对式(11),(12),(13)进行整理有:


       现在需要对(14)中的后两个式子进行求解即可(因为只需要角度部分),对于第二个式子有:


       这是复数域的解,对于第三个式子,让x=cosθ,dx=sinθdθ,λ=l(l+1),可以得到:


       求解得:


       根据(7),(15),(17),可以得到球谐函数的表达式为:


       这里的N是归一化因子,球谐函数的最终表达如下:


       其中可能会有个疑惑,为什么我们要将(14)中的第三个式子进行x=cosθ,dx=sinθdθ,λ=l(l+1)的代换而得到(16)?(17)(18)(19)中的p又是什么东西?
       代换的目的肯定是为了求解常微分方程,其中(16)就是“伴随勒让德方程”。而P_l^m 是“伴随勒让德多项式”就是“伴随勒让德方程“的解(伴随勒让德方程可以看作是勒让德方程的一般化)。其中l是一个非负整数,是伴随勒让德多项式的次数或者称之为带(band),表示多项式中所有单项式指数次数和的最大值;m是指数或者称之为阶(order),它的取值范围为0~l,表示该带内勒让德多项式导数的阶。这样就可以通过l与m去控制球谐函数的频率分布与相似频率范围下的方向特征,一般伴随勒让德方程的定义域是[-1,1]。
       通常只考虑实数域的球谐函数,所以(19)中的虚部部分可以去掉,只考虑实数域的球谐函数可以表示为:


       每一带每一阶的伴随勒让德多项式都会对应一个球谐函数,维基百科上列出了前十个条带的球谐函数计算式[4],可视化后如下:


       附上维基上更详细的推导过程[5]:https://en.wikipedia.org/wiki/Spherical_harmonics
2.3,球谐光照的计算过程
       球谐光照的核心就是对球面亮度信号的编码与重建,经过全局光照的计算后物体表面每一点会得到一个球面的亮度信号,它与整个环境光照有关,但不可能对每一点的光照信息全部存储一张图,所以需要对这个照值进行编码,重建时利用编码快速构建出原始渲染方程的结果,这是球谐光照的核心思想。所以整套球谐光照计算应当包含两个步骤——投影与重建[6]。
2.3.1,投影
       球谐投影即把原始渲染方程改用球谐基函数进行近似替代,首先需要将原来的渲染方程离散化得到f(θ,Φ)【IBL部分已经分析过,分别为航向角与天顶角】,然后通过重要性采样求得蒙特卡洛积分式,然后将蒙特卡洛积分结果与球谐函数Y_l^m(θ,Φ)进行卷积和运算,也就是计算f(θ,Φ)在Y_l^m(θ,Φ)上的投影,求出加权系数c_l^m。现设投影后的近似函数g(θ,Φ),则球谐系数为:


       然后将球谐系数按照一定的格式进行编码,存储到本地的文件中,数据的存储格式会在应用阶段另作分析。
2.3.2,重建
       重建的过程即通过解码球谐系数,求得球谐系数与球谐基的点积和,就得到了原有渲染方程的近似解,即g(θ,Φ)的数值解。




       为什么只是近似解呢,我们知道l控制着球谐函数的频率,如果基函数足够多,则计算结果与原方程拟合度越高,但随着基函数的带的增加运算量也会快速增加,这样球谐光照的优势就没了。所以采取一个折中的方案,即只使用前三阶共9个球谐基,忽略更高频的光照信息。当然这样是有损的,但是更考虑更高频的光照信息对整体贡献不大,综合计算成本这种割舍是有意义的。
2.4,引擎应用
       球谐光照在引擎里的应用就是光照探针了,在不同引擎里叫法不尽相同但本质上是一个东西。UE4引擎中一个应用是VLM(光照重要性体积):


       VLM的前身是ILC(间接光照缓存),本质上都是探针阵列(球谐点阵),区别是VLM通过调整探针密度的方式重新分配了区域内的有限光子数。主要用来为动态物体提供环境光,会以16进制的方式编码在BuildData中。
三,分割近似求和的IBL方案
       解决完环境漫反射计算后,来看下高光反射。高光反射分布的波瓣是有方向的,高光积分不能直接进行卷积,也无法以图的方式存储计算数据。为了能用IBL的思路计算环境高光反射,Epic的图形TD Brian Karis在SIGGRAPH 2013[7]上提出了Split Sum Approximation(分割近似求和)计算IBL的思想:


       顾名思义,一是分割,一是近似,最后shader中求和
       首先将渲染方程的高光反射部分进行分割,所谓分割,也就是数学拆分:



3.1,预过滤环境图
       首先看第一部分,就是入射光在半球内的积分,第一部分的积分变量与v,n有关,还是无法存储到一张纹理中:


       所以开展第二步—近似,假设v = n = r,积分式进一步简化如下:


       这样就比较方便计算了,写成蒙特卡洛积分的形式如下:


       概率密度函数P影响了蒙特卡洛积分的收敛速度,对于每一个方程数学上理论讲都会存在一个最佳的PDF使得整个蒙特卡洛积分过程达到最快的收敛速度,但我们还要考虑哪种方法拟合度更高。这涉及到重要性采样方案的选择,Epic使用Hammersley 序列生成采样点作为输入计算重要性采样,然后使用GGX分布进行球形采样向量的处理,最后执行一个预滤波卷积,相关伪代码如下:


       卷积的结果会得到一张图,被称为预滤波环境贴图,有点类似于辐照图,但预滤波过程考虑了粗糙度,随着粗糙度的增加参与环境贴图卷积的采样向量会更分散,从而导致反射更模糊


         所以它应当是Mip格式的,采样时则根据不同的粗糙度层级采样不同的level。
3.2,BRDF积分运算
        至于分割后的第二部分,也就是BRDF的积分需要继续进行“分割”,把Cook-Torrance的BRDF拆分为g项与f项:


        假设t=(1-h.v)^5,间接光照的镜面反射渲染方程第二部分可以写作


       上述式子最终计算得到:


       经过这样的数学变换,目标积分式就变成了F_0的一个倍率与一个偏差之和。而这两个积分计算完毕后实际上只与α与cosθ_v有关,恰巧这两个变量都在0~1之间,刚好可以作为纹理坐标。因此可以很方便的将这两个变量作为输入参数,对上面两个积分进行预计算,然后将结果保存到贴图中。这张贴图计算后如下:


       横坐标存到是cosθ_v,纵坐标存的是Roughness,图中的每一个像素记录的是该粗糙度,cosθ下的积分计算结果,也就是高光BRDF的积分结果。
       生成这张图的伪代码如下:


       这样通过IntegrateBRDF函数就能计算出一张LUT图,运行时直接采样:


       有了LUT图,有了第一部分计算的预过滤环境图,通过两张图的采样求和就能完成整个环境光照的反射高光捕获。
3.3,引擎应用
3.3.1,反射探针
      引擎中应用之一就是反射探针,如下:



       UE4提供了3类反射探针:
    Sphere Reflection Capture:适用于大多数场景,效果适中,计算量小Box Reflection Capture:适用于走廊与矩形空间,计算量适中Planer Reflection Capture:小范围平面反射效果好,实时运算,能耗较高(渲染两遍level)
       它们之间的关系是:     
       PRC的精度高于SSR但能耗较高(PRC>SSR>SRC)【平面反射能耗最高是因为渲了2遍场景】,SSR由于无法反射屏幕外对象在图像边缘会发生反射泄露,并且会糊一些,但反射位置是一直正确的。引擎默认SSR优先级高于PRC,并且使用SSR混合其他反射效果做的更真实的反射
      我们自己在使用时的原则是:
      PC端优先使用SSR,尽量规避PRC,局部可使用SRC进行补偿但探头混合不能超过8个(移动端最高支持3个,但建议不要超过2个)
3.3.2,Skylight
       UE4的SkyLight效果很好,也一直被誉为业内标杆,由此甚至在一些人心目中蒙上了一层神秘的面纱,但其实其本质上还是“天光球谐+天光IBL”,与一般探针不同之处在于它可以将遥远天穹的环境光反射到当前物体上。
       关于天光具体的工作原理会在本专栏第三章:Disney原则的PBR——物理照明一文中进行详细的分析
       UE4引擎中关于IBL计算与应用的相关文件如下:



Reference:
[1]《全局光照技术》秦春林.著 http://www.thegibook.com/
[2] 《LearnOpenGL》: https://learnopengl.com/
[3] 生成Irradiance map的工具:https://seblagarde.wordpress.com/2012/06/10/amdcubemapgen-for-physically-based-rendering/
[4] 维基百科上前十个条带的球谐函数计算式:https://en.wikipedia.org/wiki/Table_of_spherical_harmonics
[5] 球谐函数的详细推导过程:https://en.wikipedia.org/wiki/Spherical_harmonics
[6] 《Unity内建着色器源码剖析》球谐光照部分
[7] SIGGRAPH 2013 《Real Shading in Unreal Engine》:
https://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2021-8-13 17:35 | 显示全部楼层
写的真好。文路清晰,层层递进,看起来不累很上瘾。
顺着这片文章点开很多知识点,感谢。[赞][赞][赞]
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-15 13:56 , Processed in 0.091589 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表