因为上一篇已经着重介绍过diffuse+specular,本篇重点就放在间接光照这部分。先简单介绍下:StandardLit我采用的是Lambert+D(GGX)+F(Schlick)+ V (SmithJointApprox),也可以用迪士尼那一套PBR:
迪士尼的Diffuse:
D项:
F项上一章介绍过,V(G)项:
间接光照
漫反射
这部分后面等理论篇整理好再编辑。。。
镜面反射
这部分的理论基础我提前整理了出来,见以下文章:
基于物理的光照:环境光照 (Enviroment Lighting)(1)
根据理论,我们需要先计算反射向量R
第一步,预过滤环境映射
由基础理论我们知道,我们采样的环境贴图需要得到其filter的大小,也就是模糊后的图片,也就是Mipmap层级。如何获取呢?在URP中 GlossyEnvironmentReflection()有实现:
half mip = PerceptualRoughnessToMipmapLevel(perceptualRoughness);
关于PerceptualRoughnessToMipmapLevel方法背后的数学原理,我单独写了一篇文章,有兴趣的小伙伴可以看一看。
有了filter的大小,我们就可以采样这张环境光照的cubemap了。值得一提的是cubeMap必须采用三线性插值,这是因为我们求得的miplevel是一个小数,需要两张相邻层级的mipmap先做一次双线性插值,然后再在两张之间再做一次插值才得到结果。
当然说了这么多只是在解释Unity URP的工作原理,实际用起来我们直接拿来用就好。
第二步,拟合
理论篇的第二步是通过采样一张LUT贴图来做的。
UE5里提供了一种数学拟合的方式,节省了一次采样计算,我们可以直接拿来用
AO
IBL一大问题就是漏光非常严重,为了解决漏光问题,我们引入AO(严格意义应该叫SpecularOcclusion,不过都这么叫习惯了,明白就好)。
Unity 中提供了几种处理AO的方式,参考URP实现方案后我采用了下面几种:首先是GTAOMultiBounce:
这个方法来源于SIGGRAPH2016动视的一篇分享(原文见专栏导言附录2),拟合一个函数,将单次反弹结果映射到多次反弹结果。这个方法可以防止一些表面(尤其是金属)遮挡过黑。这部分在GAMES202介绍微表面模型时提到过,当我写到这部分的理论时候我会详细介绍一下,总结来说就是为了保持能量守恒:
对于不同的albedo,拟合的值也不同
其次是GetSpecularOcclusionFromAmbientOcclusion:()
这是一个从环境遮挡派生出镜面遮挡的方法,此方法来源于Frostbite的PBR(原文见专栏导言附录3):
值得一提的是specular occlusion本身并不“PB”,而是一个基于经验的trick。其他的图中解释的非常清楚,就不再赘述了。