PBR理论基础3:基于图像的光照(上)

 

一、基于图像的光照(Image based lighting, IBL)

只要了解了下面这些,就很容易理解基于图像的光照了:

  1. 直接光与间接光的区别(UnityShader9:光照基础回顾
  2. PBR理论基础2:光照模型与微面元理论
  3. PBR理论基础1:辐射度与BRDF
  4. 动态环境映射:UnityShader18.1:立方体贴图(下)
  5. OpenGL基础50:HDR

在 PBR 的前两章,光照考虑的都是直接光源,这一章考虑的就是间接光,或者说是环境光

一样是和环境立方体贴图有关,基于图像的光照就是将环境立方体贴图的每一个纹理像素作为光源,并在前面渲染方程中直接使用它,以实现物体融入环境的效果

换句话说,IBL 最终的目的就是对于确定的反射微表面点 p,求出 

L_o(p,\omega_o) = \int\limits_{\Omega} (k_d\frac{c}{\pi} + k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i 的近似值

其中 L_i(p,\omega_i) 为半球上任意光源方向 \omega_i 在 p 点的辐射率,这个值来源于向量 \omega_i 对环境立方体贴图的采样结果

在计算上和前面两张计算定点光源最大的区别就是:前者只需要考虑单一方向也就是光源方向,而现在要考虑整个球面上所有的方向,得到真正的积分结果

 

 

二、环境立方体贴图

先考虑作为输入的环境立方体贴图,这也是第一步

这一章中就曾经尝试生成过环境立方体贴图,以场景中任意点 p 为中心生成的环境立方体贴图当然都是不同的,这样就会出现两个问题:

  1. 场景中的每一个物体都需要生成对应的环境立方体贴图,这个不太现实,更何况每个物体又不是以“点”存在的
  2. 如果场景中有多个动态物体,如何计算光线在多个物体表面的多次反射结果

一个比较幸运的是,这两个问题在一章中都先不考虑,我们就只考虑固定点 p 的光照情况就可以了,这两个问题可以后面去解决(对于问题①,使用反射探针技术,对于问题②,上光线追踪吧)

应用 HDR 的立方体贴图

如果固定了点 p ,那么我们就只需要考虑唯一的一张立方体贴图,但由于 PBR 的大部分输入基于实际物理属性和测量,因此为入射光值找到其物理等效值非常重要

之前应用的立方体贴图都是以纹理存储的,颜色范围为 [0, 1],也就是 LDR,这样的纹理用于视觉输出还可以,但是用于物理输入参数就非常不科学了(想想灯泡和太阳都一个亮度 1.0),因此这次所需要的环境立方体纹理最好是要考虑了 HDR 的

这涉及到一个全新的文件格式:.hdr 文件,它存储了一张完整的立方体贴图,所有六个面数据都是浮点数。和其它图片文件格式一样,它也是用 RGBA 32位信息存储的每一像素,只不过它的 alpha 通道存放的是指数(尽管这确实会导致精度损失,但是确实有效率)

  • .hdr 文件需要有对应的解析程序,以将每种颜色重新转换为它们的浮点数等效值
  • 除此之外还有一种文件格式为 .exr,原理和 .hdr 文件类似,都可以用于存储 HDR 纹理
Unity中HDR颜色设置参数

 

 

三、漫反射项预计算

对于反射方程:

L_o(p,\omega_o) = \int\limits_{\Omega} (k_d\frac{c}{\pi} + k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i

\small L_o(p,\omega_o) = k_d\frac{c}{\pi}\int\limits_{\Omega} L_i(p,\omega_i) n \cdot \omega_i d\omega_i + \int\limits_{\Omega} (k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i

先考虑它的漫反射项,常数部分很简单可以先拿掉,这样目的就成了先计算   \int\limits_{\Omega} L_i(p,\omega_i) n \cdot \omega_i d\omega_i   这一部分

卷积结果

计算积分最简单的一个方法就是暴力枚举所有可能的 \omega_i 求出它们的结果 L_i(p,\omega_i) n \cdot \omega_i,最后求个平均就可以,但是 \omega_i 很明显有无数个,因此这样只能退而求其次:①通过对半球 \Omega 上的大量方向进行离散采样,对这些采样的结果求出其辐射度取平均值,以作为所有 \omega_i 采样的平均结果(采样次数越多结果越精确)

还有一个难点,上述的微分单元是一个个无穷小的立体角,也就是下图:

图片来源: LearnOpenGL

好在,②立体角可以使用倾斜角 \theta 和航向角 \phi 来表示,就可以得到:

\int\limits_{\Omega} L_i(p,\omega_i) n \cdot \omega_i d\omega_i =L_o(p,\phi_o, \theta_o) = \int_{\phi = 0}^{2\pi} \int_{\theta = 0}^{\frac{1}{2}\pi} L_i(p,\phi_i, \theta_i) \cos(\theta) \sin(\theta) d\phi d\theta

其中 \cos(\theta) 来源于 n \cdot \omega_i,当单位向量 \omega_i 与单位法线 n 重叠时,\theta = \frac{\pi}{2}。又由于当采样区域朝中心顶部会聚时半球的离散采样区域变小,为了平衡较小的区域贡献度,需要使用 \sin(\theta) 来权衡区域贡献度

结合①和②,就可以得到一个我们可以去计算的离散版本,也就是黎曼和公式

L_o(p,\phi_o, \theta_o) = \frac{1}{n_1 n_2} \sum_{\phi = 0}^{n_1} \sum_{\theta = 0}^{n_2} L_i(p,\phi_i, \theta_i) \cos(\theta) \sin(\theta) d\phi d\theta

最后当然不要忘记常数项 \small k_d\frac{c}{\pi},对于折射系数 k_d 一般是由菲涅尔项得出的,而由于环境光来自半球内围绕法线 n 的所有方向,因此没有一个确定的半向量来计算菲涅耳效应,为了模拟菲涅耳效应,这儿可以用法线和视线之间的夹角来计算菲涅耳系数

好了大功告成,接下来就是考验代码功底,由于这是理论章所以就不上代码了,后面有机会的话会有实践的文章

辐照度图

如上对于任意采样方向(观察方向)\omega_o,都可以得到其近似辐照度 L_o(p,\omega_o),又由于观察点 p 确定,L_o(p,\omega_o) 值也是不会变的,因此我们可以对其进行预计算,并将结果保存在另一张立方体贴图中,这就是辐照度(irradiance)图的由来

图片来源: LearnOpenGL

 

参考资料:

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值