在基于物理的渲染(PBR)中,为了在实时(Real-time)环境光照(IBL)下求解渲染方程,由于半球积分计算量太大,业界(由 Epic Games 在虚幻引擎 4 中普及)广泛采用了分离求和近似(Split-Sum Approximation)。 这个方法将原本复杂的环境光积分公式拆分成了两大部分:漫反射部分镜面反射部分(并且镜面反射又被拆成了两个独立的项)。

{{< link title=“Cook-Torrance” url="/posts/27b25994" icon="/favicon.ico" >}}


1. IBL_Irradiance (辐照度贴图) —— 负责【漫反射 IBL】

在处理环境光时,漫反射(Diffuse)部分的渲染方程是:

$$L_{diffuse}(p, V) = k_d \frac{c}{\pi} \underbrace{ \int_{\Omega} L_i(p, w_i) (N \cdot w_i) dw_i }_{\text{IBL\_Irradiance}}$$
  • 它计算了什么: 它计算了所有方向的环境光($L_i$)在法线半球上的加权平均值(仅受法线朝向影响)。
  • 具体表现: 我们在预处理阶段,针对每一个可能的法线方向 $N$,将环境贴图的颜色进行积分,存入一张模糊的 Cubemap 中。在实时渲染时,只需用着色点的法线 $N$ 去采样这张图,就能直接得到积分的结果。

镜面反射的分离求和 (Specular Split-Sum)

镜面反射部分的公式非常复杂,包含了微表面 BRDF(高光项)和环境光的交互:

$$L_{specular}(p, V) = \int_{\Omega} L_i(p, w_i) f_{spec}(w_i, V) (N \cdot w_i) dw_i$$

因为 $f_{spec}$ 依赖于粗糙度(Roughness)和视角($V$),直接积分是不可能的。因此 UE4 提出了分离求和近似,将这个积分强行拆成两个乘积项:

$$L_{specular} \approx \underbrace{ \left( \int_{\Omega} L_i(p, w_i) p(w_i, V) dw_i \right) }_{\text{IBL\_Prefilter}} \cdot \underbrace{ \left( \int_{\Omega} f_{spec}(w_i, V) (N \cdot w_i) dw_i \right) }_{\text{IBL\_BRDFLUT}}$$

注:$p(w_i, V)$ 是基于粗糙度的概率密度函数。


3. IBL_Prefilter (预滤波环境贴图) —— 负责镜面反射的【光照部分】

重要性采样 对应拆分公式的左半边:

$$\text{Prefilter}(R, V) = \underbrace{ \int_{\Omega} L_i(p, w_i) p(w_i, V) dw_i }_{\text{IBL\_Prefilter 预滤波}}$$
  • 它计算了什么: 它计算了受材质粗糙度(Roughness)影响的环境光输入。粗糙度越高,高光波瓣越宽,收集到的光线方向就越广,看起来就越模糊。
  • 具体表现: 在预处理时,我们利用不同的粗糙度级别对环境光 Cubemap 进行不同程度的模糊处理,并将结果分别存在 Cubemap 的不同 Mipmap 层级中(例如:Mip 0 是清晰的原图,Mip 4 是极度粗糙模糊的图)。实时渲染时,根据当前像素的粗糙度和反射向量 $R$ 去采样对应的 Mipmap 层级。

4. IBL_BRDFLUT (BRDF 查找表) —— 负责镜面反射的【材质/几何部分】

对应拆分公式的右半边:

$$\text{BRDFLUT}(N \cdot V, \text{Roughness}) = \underbrace{ \int_{\Omega} f_{spec}(w_i, V) (N \cdot w_i) dw_i }_{\text{IBL\_BRDFLUT 积分}}$$
  • 它计算了什么: 假设周围环境是纯白光($L_i = 1$),它单纯计算了Cook-Torrance BRDF(法线分布 D 和几何遮蔽项 G)对最终结果的贡献
  • 具体表现: 通过代入菲涅尔的 Schlick 近似公式($F = F_0 + (1-F_0)(1-\cos\theta)^5$),这个积分被巧妙地化简为 $F_0 \times \text{Scale} + \text{Bias}$ 的形式。这两个值(Scale 和 Bias)只取决于两个变量:表面粗糙度(Roughness)和视线夹角余弦值($N \cdot V$)。
  • 因此,我们可以预先用数学方法算好所有结果,存在一张 2D 的查找表纹理(LUT,Look-Up Texture)中。U 轴通常代表 $N \cdot V$,V 轴代表粗糙度。采样出的 Red 通道作为 Scale,Green 通道作为 Bias。

在着色器中的最终组合

在最终的实时 Shader 里,这三者的配合等同于完成了整条渲染方程的近似计算,伪代码如下:

GLSL
// 1. 获取漫反射 IBL
vec3 diffuseIBL = texture(IBL_Irradiance, N).rgb * albedo * kD;

// 2. 采样预滤波环境光 (光照积分部分)
vec3 prefilteredColor = textureLod(IBL_Prefilter, R, roughness * MAX_MIP_LEVEL).rgb;

// 3. 采样 BRDF LUT (材质积分部分)
vec2 brdf = texture(IBL_BRDFLUT, vec2(max(dot(N, V), 0.0), roughness)).rg;
// 根据 F0 重构完整的 BRDF 积分
vec3 specularBRDF = F0 * brdf.x + brdf.y;

// 4. 镜面反射 IBL = 光照积分 * 材质积分
vec3 specularIBL = prefilteredColor * specularBRDF;

// 5. 最终结果
vec3 ambient = diffuseIBL + specularIBL;
点击展开查看更多

版权声明

作者: Cheyne Xie

链接: https://chaim.eu.org/posts/10f50c4b/

许可证: CC BY-NC-SA 4.0

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Please attribute the source, use non-commercially, and maintain the same license.

开始搜索

输入关键词搜索文章内容

↑↓
ESC
⌘K 快捷键