跳转至

2 Real-time Environment Mapping

2.1 Shading from environment lighting

回忆一下环境光照的概念:环境光照(Environment Lighting)是用一张全景图表示来自所有方向的远处光照,常用球面映射(Spherical Map)或立方体贴图(Cube Map)来存储和应用。

再回忆一下渲染方程

\[ L_o(\mathbf{p}, \omega_o) = \int_{\Omega^+} L_i(\mathbf{p}, \omega_i) \cdot f_r(\mathbf{p}, \omega_i, \omega_o) \cos \theta_i\cdot V(\mathbf{p}, \omega_i) \, d\omega_i \]

\(\Omega^+\) 就是该点的正半球面,如果不考虑阴影的话,就将 \(V(\mathbf{p}, \omega_i)\) 给去掉即可。

渲染方程的求解在 GAMES101 中也有提及,基本的解决方法就是使用蒙特卡洛积分的方式来解决。这个方法最严重的问题就是采样量的问题,每个片段都需要大量的采样,以得到该点的颜色值,这在实时渲染的场景下,显然是不可取的。


对于上面那个式子,我们有一个观察

  • 如果 BRDF 是 glossy 的,那么有值的部分会很小,即 “支撑集(support)”很小。
  • 如果 BRDF 是 diffuse 的,那么整个 BRDF 是 smooth的

由上述观察,我们很容易联想到上一小节所讲的公式

\[ \int_{\Omega} f(x) g(x) \, dx \approx \frac{\int_{\Omega_G} f(x) \, dx}{\int_{\Omega_G} dx} \cdot \int_{\Omega} g(x) \, dx \]

这里 \(f(x)\) 的积分域是 \(G\)

那么我们就可以对原先的渲染公式进行拆分

\[ L_o(\mathbf{p}, \omega_o) \approx \frac{\int_{\Omega_{f_r}} L_i(\mathbf{p}, \omega_i) \, d\omega_i}{\int_{\Omega_{f_r}} d\omega_i} \cdot \int_{\Omega^+} f_r(\mathbf{p}, \omega_i, \omega_o) \cos \theta_i \, d\omega_i \]

后续具体的计算方法,在 11 PBR - RoderickShao's NoteBook 中有详细解释,这里不再赘述。

对于环境光照下的阴影生成,这是十分困难的。我们可以将环境光照问题看做是一个多光源的问题,那么如果要生成阴影的话,就需要为每一个光源都生成一个 Shadow map,这显然是我们不愿意做的。我们还可以将 visibility 看做渲染方程中的一项,这个部分的采样也是十分困难的。

在工业界,我们可以选择其中最为主要的几个光源来做相应的 shadow map 来生成阴影。

在学术界,有以下相关的研究

  • Imperfect shadow maps
  • Light cuts
  • RTRT (might be the ultimate solution)
  • Precomputed radiance transfer 该方法在下一节中进行介绍

2.2 Precomputed Radiance Transfer

首先我们介绍一下球谐函数的概念

球谐函数是定义在球面上的一组正交基函数,可以看作是一维傅里叶级数在球面上的二维推广:正如傅里叶级数用不同频率的正弦/余弦波组合来表示任意一维信号,球谐函数用不同阶数( \(l\) )和方向( \(m\) )的基函数组合来表示定义在球面上的任意函数(如光照、辐射强度等);

图中 \(l\) 表示频率阶数\(l=0\) 是直流/常量, \(l\) 越大频率越高、细节越丰富), \(m\) 表示方向分量\(−l≤m≤l\)

球谐函数的每个基函数 \(B_i(ω)\) 都与勒让德多项式相关联

使用流程分为两步:

  • 投影(Projection)是通过积分,将原始函数(如光照)分解为各基函数的系数;
\[ c_i=\int_Ω f(ω)B_i(ω) dω \]
  • 重建(Reconstruction)则是用这些(通常截断为低阶的)系数与基函数线性组合来近似恢复原始函数

任何乘积积分 \(\int_Ωf(x)g(x)dx\) 本质上都可视为一种滤波操作,而积分结果所呈现的有效频率受限于参与运算的各函数中最低的那个频率——这意味着你用低频基函数去“探测”信号时,只能捕捉到其低频成分,高频细节会被自然滤除。

而在渲染方程中,当材质是 diffuse 的时候,相当于说 \(g(x)\) 是一个低频的信号,捕捉光源的时候,只能捕捉到相应的低频信息。

通过实验,我们可以知道一个 diffuse 材质,常常使用三阶球谐函数就可以基本表示

也就是说,对于光源,我们实际上使用三阶球谐函数去模拟也能得到很好的效果


下面我们将开始介绍 PRT

我们将渲染方程中的项分为 lighting 和 lighting transport

\[ L(\mathbf{o}) = \int_{\Omega} \underbrace{L(\mathbf{i})}_{\text{lighting}} \cdot \underbrace{V(\mathbf{i}) \, \rho(\mathbf{i}, \mathbf{o}) \, \max(0, \mathbf{n} \cdot \mathbf{i})}_{\text{light transport}} \, d\mathbf{i} \]

PRT 的基本思路如下:

  • 预计算阶段,将光照以及传输函数(含可见性、BRDF、几何项)投影到一组球谐函数上并存储系数;
  • 运行阶段,通过简单的点积(漫反射)或矩阵-向量乘法(高光)快速合成最终颜色——从而实现实时全局光照

对于光照,我们将其投影到球谐函数上,可以得到一系列的系数

\[ L(\mathbf{i}) \approx \sum_{i} l_i \cdot B_i(\mathbf{i}) \]

对于传输函数 (light transport),如果是 diffuse 的情况,brdf 是常值,那么 light transport 的值与相机视角是无关的,此时 light transport 项只与入射方向有关,那么相应的结果可以如下表示

\[ L(\mathbf{o}) \approx \rho \sum_{i} l_i \int_{\Omega} B_i(\mathbf{i}) V(\mathbf{i}) \max(0, \mathbf{n} \cdot \mathbf{i}) \, d\mathbf{i} \]

\(T_i = \int_{\Omega} B_i(\mathbf{i}) V(\mathbf{i}) \max(0, \mathbf{n} \cdot \mathbf{i}) \, d\mathbf{i}\),那么在运行阶段只需要计算下面这个公式

\[ L(\mathbf{o})\approx \rho \sum_{i} l_i T_i \]

这是 diffuse 的情况,light transport 只是一个关于 \(i\) 的二维函数,最后运算的时候计算的是一个向量的乘积。

那么对于 glossy 的情况,此时 brdf 与入射方向和出射方向都有关系,也就是说 light transport 是一个四维的函数。首先我们以 \(\mathbf{i}\) 为主元,将 light transport 投影到球谐函数上,最后得到的 \(T_i(\mathbf{o})\) 系数是与 \(\mathbf{o}\) 相关的函数。那么对于这个 \(T_i(\mathbf{o})\) 再将其投影到球谐函数上,可以得到

\[ T_i(\mathbf{o}) \approx \sum t_{ij} B_j(\mathbf{o}) \]

最后得到运行时的公式就为

\[ L(\mathbf{o})\approx \sum(\sum l_it_{ij})B_j(\mathbf{o}) \]


关于这个传输系数的预计算,我们可以将其理解为预计算光传输 = 用每个球谐基函数当“虚拟光源”去照射场景,记录下每个点的响应值,然后这些响应值构成传输系数矩阵。

相当于就是把自然光正交分解了,然后每个基在这个物体的照射情况我都预计算(这里的预计算可以使用光线追踪等技术)过了,这样运行的时候只需要结合自然光分解之后的系数即可


PRT 虽然效果是不错的,但是仍然存在很多的问题

  • 受球谐函数(SH)低频特性限制,仅能表现柔和的全局光照效果,无法还原高频细节(如锐利阴影或镜面高光),需更高阶数(如 N=25 以上)才能逼近真实,但会急剧增加数据量;
  • 它支持动态光照却要求场景与材质静态——一旦物体移动、变形或材质改变,预计算的传输系数即失效;

支持动态光效的原因是利用球谐函数具有旋转不变性,支持光照或物体旋转时高效变换系数

接下来在 PRT 方向,有以下相关工作

  • 寻找其他基函数,得到更好的结果
  • 将点积升级为三重积以支持高光材质;
  • 推动场景与材质从静态走向动态(如变形角色、可编辑环境);并拓展至半透明、毛发等复杂效果;
  • 同时探索用解析计算替代预计算,减少存储依赖、增强灵活性

评论区

对你有帮助的话请给我个赞和 star => GitHub stars
欢迎跟我探讨!!!