学习记录 以前遇到球谐,就只知道unity中可以直接用SH9函数获取结果,为什么要用这个来计算间接漫反射,为什么是3阶球谐。这些疑问学习后才逐渐了解。其数学推导的过程可能实际应用中不太会涉及,但是了解原理和过程还是挺值得学习的
跟着这位老师的思路学习的
球谐光照——辐照度照明 - 知乎
球谐函数是拉普拉斯方程分离径向后,角度部分通解的正交项
那么就需要了解球坐标系下的拉普拉斯方程分离变数的过程,将角度部分分离
通过俩步分离变数 得到3个耦合的常微分方程
用表示连带勒让德函数,所以之前球谐函数通解的复数形式就可以表示为
前几次的球谐函数可以计算求得
任何一个球面函数,可以以球谐函数作为基底进行广义傅里叶展开,就可以理解为球谐函数作为基函数,求傅里叶系数的过程,生成球谐系数的过程称为投影,根据生成的球谐系数来表示球面函数的过程称为重建
对进行广义傅里叶展开,我们要求的就是广义傅里叶系数
根据BRDF的反射等式,可以知道
上面的俩等式非常重要,我们计算出光照函数和传递函数的球谐系数,通过俩种系数的内积就可以计算环境贴图对模型的漫反射光的贡献。
首先看传递函数的球谐系数,计算过程略过 根据连带勒让德函数正交性和奇偶性计算
再结合之前的最终光照计算公式
可以得知传递函数的球谐系数随次数增加衰减的非常快,重建低频信息的时候不需要很高的次数,这就是目前游戏引擎主流的3次9个球谐系数的做法。
到之前为止主要是说明球谐函数的计算过程和为什么游戏一般采用三阶球谐,由于论文中都给出了前几阶的球谐系数,一般直接拿来用就行。下面主要涉及到应用层面的是光照函数部分的球谐系数计算
然后是光照函数的球谐系数计算,将之前的式子转化为求和的形式
那么,现在前三次球谐函数已知,纹素信息已知,我们需要求的就是纹素的立体角和根据纹素坐标计算法线的信息了。
下面部分的计算,Git有大佬已经实现了,算法基本一致的
GitHub - pieroaccardi/Unity_SphericalHarmonics_Tools: Two tools for Unity to deal with spherical harmonics
首先是立体角的计算
然后是纹素和法线的对应关系
Cubemap Texture - OpenGL Wiki
可以理解为一个边长为2的立方体,坐标原点位于中点,每个轴值(-1,1)。坐标系为左手坐标系,然后需要计算的是每一个面的纹素的uv坐标对应在原点坐标系下的值来表示法线方向。需要注意的是cube的每一个面的uv零点的值是位于左上角的,定义如此。
上图的表示感觉不直观,还是git这个的实现比较直观,根据纹素位置的uv计算出(-1,1)的轴向的值。
至此,球谐函数,纹素立体角和法线对应关系都知道了,就可以计算出光照函数的球谐系数了,环境图的RGB每个通道单独计算。
实际的应用是拿一张HDR的环境图,然后用球谐函数通过上诉计算求得光照函数的球谐系数,然后再在材质中通过这27个系数和传递函数的球谐系数就可以内积计算球谐漫反射部分的信息。
知道了传递函数和光照函数的球谐系数和球谐函数,就可以通过这些信息来重建辐照度信息了。
设
上式转化为
根据之前的传递函数的球谐系数计算出
最终的漫反射光照为:
材质中使用上述计算还原即可
效果如下
压缩gif有噪点。。实际是没问题的
比如我想要在引擎中还原一下SP的效果,那光照信息就全部来自那张环境图。而在游戏引擎中,角色周围的环境光照肯定是和角色所在的区域相关的,用一张死的环境图就不行了。
所以游戏引擎中就提供了环境探针来存贮一个区域内的环境信息,如unity的lightprobe每一个小球都记录了周围的环境信息,物体在多个ilghtprobe间移动的时候,球谐系数会在角色周围的四个lightprobe插值计算。插值计算相关这里有提到:
Light Probes 基本理论介绍_哔哩哔哩_bilibili
所以对于游戏的美术制作流程来说,想要引擎内效果和SP一模一样的话,那可能要考虑的就是角色在环境中的间接光照始终来源于一张固定的环境图,也有项目是这样做的。但是肯定还是需要Lightprobe这样根据游戏中的环境搭建的信息更好,这样像TOD之类的效果也可以通过多套球谐烘焙插值来实现。
除开间接光漫反射的球谐这部分,间接光的镜面反射部分也有很大一块内容值得学习,这里就不提及了。下图是保证引擎和SP效果基本对齐,用一张环境图来提供IBL信息的自定义PBR材质效果,左边UE,右边SP。
参考资料:
数学物理方法 5-1.1 球坐标系下拉普拉斯方程的分离变数_哔哩哔哩_bilibili
球谐光照——辐照度照明 - 知乎
https://cseweb.ucsd.edu/~ravir/papers/invlamb/josa.pdf