PBR流程中,光照探头用于给动态物体提供间接光的漫反射信息。
内容对应某位大佬的视频:Light Probes 基本理论介绍_哔哩哔哩_bilibili
第一步:记录所有探头的数据,在烘培的时候,光照探头需要记录周围360度的颜色信息。类似反射探头的cubemap,但是由于光照探头非常多,而且cubemap占用内存太多,所以实际储存不可能用cubemap,而是储存着球谐函数某几个基函数的系数。当实际渲染进行采样的时候,用这几个系数和球谐函数基函数来还原周围的颜色信息。这里的思想和傅里叶变换是一样的,也是用几个简单的信息基函数,通过不同的系数来还原一个复杂的信号。
傅里叶变换 如下图:
c1是第一个基函数的系数,f1则是第一个基函数。通过这种组合方式还原出复杂的原信号。
而球谐函数也是,通过系数1*基函数1+系数2*基函数2....的形式来还原出光照探头原本位置的cubemap颜色信息,只不过球谐函数是建立在球坐标系上的,而傅里叶变换则是建立在二维笛卡尔坐标系上的,当然,这并不能百分百还原。总的来说,烘焙光照探头就是记录几个基函数的系数以此来记录探头周围的颜色信息。
下面是各种球谐函数的基函数示意图:
图中总共显示了四层球谐的基函数,第0层只有一个基函数,还原度最低,第二层,则有左右、上下、前后三个基函数,而下面的层数则更加的细致,还原度会更高,但是随之的性能消耗也越大。Unity里通常会使用前3~4层的基函数。
下面是还原示意图:
左下角的图,是球谐函数还原后的颜色信息,颜色越亮,高度越高。而右下角则是原本的cubemap,由于这里是用来做间接光的漫反射的,所以肉眼是区分不出来的。能够有效的减少内存的消耗。
第二步:对光照探头进行采样,在渲染中,计算某个片元的间接光漫反射颜色的时候,会取该片元附近(半径,该值可由程序员自定义)的所有光照探头,传入一个向量对每个光照探头进行采样,然后进行加权平均,越靠近片元的探头,权重越大。
下面说明是如何对光照探头进行采样的:
如上图片元A,会取到半径内的探头P0和P1。传入一个向量对P0和P1进行采样,再计算平均值即可。但是会遇到两个问题,如下:
视差问题:当传入间接光入射光方向的负方向(ω)的时候,由于片元x和P0 P1的位置不一致,所以并不能在光照探头中直接用传入的向量,而是需要进行raycast(射线检测)来算出ω与场景物体的交点,再利用交点与P0P1的球心算出对应探头的采样向量。利用这个向量对光照探头进行采样,各个光照探头采样出来的颜色加权平均后就能得到交点的颜色了,那么这个颜色也就是X片元反射出来的间接光漫反射颜色
遮挡问题:如下图P1被挡住了,对P1探头进行采样,是不能正确得到图中的Г点的颜色,所以这个时候需要剔除掉P1
下面是为何可以利用加权平均得到光照颜色的原理:
上图是PBR流程的基础函数,除去自发光部分,也可用到计算间接光漫反射上。
当处理漫反射的时候图中的(即BRDF函数)可以提取到积分外面。
下图是BRDF函数的说明:(具体学习可以去:【学习笔记】Unity PBR的实现 - 知乎)
辐照度:单位时间单位面积受到的辐射能量
辐射强度:单位时间单位立体角受到的辐射能量
辐射率:单位时间单位立体角单位面积受到的辐射能量
因为是漫反射,所以镜面反射可以忽略,然后把漫反射部分提取到积分外
把BRDF提到积分外的时候,函数变成:
由于计算积分非常困难,所以把积分的形式变成由几个精确光照效果的累加,一个光照探头就是一个精确光照,改变后的函数如下图:
其中计算某个光照探头的权重的函数则是:
w(t)是权重,t就是探头与片元的距离除以半径,t范围是0~1之间。
函数图像:
3:在Unity内置管线中,只需要调用ShadeSH9(float4(i.normal, 1)),传入片元世界空间下的法线方向即可得到该片元间接光的漫反射颜色。
源码分析可以看一下这篇文章:Unity3D的全局光照和阴影:下篇(unity3D中的球谐光照和SH球谐函数、unity实时阴影抗锯齿解决方案) | 电子创新网 Imgtec 社区