PBR · 微面元理论 · 能量守恒

Kulla-Conty 近似
能量补偿学习笔记

SIGGRAPH 2017 · Sony Imageworks · Kulla & Conty
多重散射 GGX BRDF 白炉测试 DFG LUT HLSL URP / UE5

Kulla-Conty 近似(Kulla-Conty Approximation)是基于物理的渲染(PBR)中用于解决高粗糙度材质能量丢失问题的标准方案。它由 Sony Imageworks 的 Kulla 和 Conty 在 2017 年的 SIGGRAPH 上提出。本文将从它的物理直觉、数学推导,以及在实际渲染管线(如 Unity / UE5 的 HLSL 着色器)中的工程实现来进行拆解。

01 痛点:为什么我们需要能量补偿?

标准的微面元 BRDF 模型(如 Cook-Torrance GGX)基于单次散射(Single Scattering)假设。这意味着光线击中微面元后,只反弹一次就必须进入摄像机。

但在物理现实中,随着材质粗糙度(Roughness)增加,微面元的起伏变得剧烈。光线击中微面元后,很容易弹射到相邻的微面元上,发生多重散射(Multiple Scattering)

由于标准 GGX 没有计算这些在微面元之间多次反弹后最终逃逸出来的光线,这部分能量在数学上就被当作"被吸收"而直接丢弃了,违背了能量守恒定律。

直接后果

粗糙度越高的金属或电介质,渲染结果看起来越发暗淡

🔁
根本原因

GGX 模型将多次弹射的光线统一视为"被遮挡",不再追踪这些能量的去向。

📐
数学含义

BRDF 的半球余弦积分(方向反照率)在高粗糙度时显著小于 1。

02 核心原理:白炉测试(White Furnace Test)

验证能量守恒的黄金标准是白炉测试

思想实验

假设我们将一个纯反射体(不吸收任何光线,)放进一个四面八方辐射度为 1 的纯白环境光炉子中。根据能量守恒定律,这个物体无论粗糙度多高,它反射出的总光量也应该恒为 1,在纯白背景中应该完全"隐形"。

然而,单次散射 BRDF 会在粗糙度较高时无法通过测试(反射积分小于 1,出现暗斑)。Kulla-Conty 的核心思想就是:算出单次散射丢失的这部分能量,并构造一个新的多次散射 BRDF 波瓣 ,把这部分能量精准地补回去。

03 Kulla-Conty 补偿的数学推导

推导过程分为两步:先推导理想纯反射体(无菲涅尔吸收),再引入菲涅尔项。


3.1 理想反射体的多重散射波瓣(

首先,我们定义单次散射的方向反照率(Directional Albedo) ,即从观察方向 看去,单次散射能反射出的总能量比例:

单次散射方向反照率

由于理想反射体应该反射所有能量,那么丢失的能量就是 。我们需要构造一个多重散射波瓣 ,使其积分恰好等于丢失的能量:

为了满足微面元理论的亥姆霍兹互易性(即 ),Kulla 和 Conty 将 假设为分离变量的形式( 为归一化常数):

将上式代入积分来求解 ,并提取出常数项

将积分拆开,考虑到半球余弦积分 ,且定义 在半球上的余弦加权平均为 (即 ),我们可以得到解:

归一化常数求解

因此,理想反射体的多重散射项为:

最终理想反射体多重散射项

3.2 引入菲涅尔吸收(

实际材质有菲涅尔效应,光线每次碰撞都会被材质吸收一部分。假设单次碰撞的平均菲涅尔反射率为

光线在微面元"峡谷"中每次弹射,都会有 比例的能量逃逸,剩下的 比例的光线继续在内部弹射,并在下一次碰撞时被 再次衰减。这是一个无限等比数列(Geometric Series)求和的过程。把所有由于多重弹射逃逸出来的能量累加起来,得到多重散射的颜色乘子

多重散射颜色乘子
完整能量守恒 BRDF
04 在引擎中的工程实现 (HLSL)

在工业界实战中(如 Unity URP/HDRP 或 UE5 的管线),不可能实时去计算上述复杂的半球积分。主流做法是依靠预计算(Precomputation)低阶多项式拟合

核心优化策略

不引入额外贴图,完美复用现有的基于图像的照明(IBL)环境光 DFG LUT,以极低开销完成补偿。

// ── Step 1: E(v) / E(l) from DFG LUT ────────────────────────
// uv.x = NdotV, uv.y = Roughness
float2 dfg  = DFG_LUT.SampleLevel(sampler_DFG_LUT, uv, 0).rg;
float  E_v  = dfg.r + dfg.g;   // F0=1 假设

float2 dfgL = DFG_LUT.SampleLevel(sampler_DFG_LUT, uvL, 0).rg;
float  E_l  = dfgL.r + dfgL.g;

// ── Step 2: E_avg 多项式拟合 ────────────────────────────────
float GetEavg(float r) {
    // 具体系数由离线曲线拟合得出,各引擎略有不同
    return 1.0 - (r * 0.05 + r * r * 0.45);
}
float E_avg = GetEavg(roughness);

// ── Step 3: F_avg(Schlick 解析近似)───────────────────────
float3 F_avg = F0 + (1.0 - F0) / 21.0;

// ── Step 4: 组装最终多重散射项 ──────────────────────────────
float3 f_ms_color  = (F_avg * E_avg) / (1.0 - F_avg * (1.0 - E_avg));
float  f_ms_factor = (1.0 - E_v) * (1.0 - E_l) / (PI * (1.0 - E_avg));

float3 multiple_scattering = f_ms_color * f_ms_factor;

// ── Final BRDF ──────────────────────────────────────────────
float3 final_brdf = single_scattering_brdf + multiple_scattering;
05 符号速查表
符号 含义 取值 / 说明
单次散射方向反照率从 DFG LUT 读取(R + G),取值 [0, 1]
的余弦加权半球均值仅依赖粗糙度,用多项式拟合
菲涅尔反射率的半球平均
标准单次散射 BRDFCook-Torrance GGX
多重散射补偿波瓣Kulla-Conty 构造,满足互易性
多重散射颜色乘子无限等比数列求和,包含菲涅尔衰减

核心思想总结

Kulla-Conty 近似的本质,是通过宏观的能量守恒要求(白炉测试),逆向反推出微观上多重散射应该补偿的能量分布。它以极其优雅的方式解决了高粗糙度材质发暗的视觉缺陷,而且在工程落地时能够完美复用引擎中现存的 DFG LUT,开销极低。目前它已成为现代实时渲染(如 Unity/UE5)和离线路径追踪器处理微面元多次散射的底层标配逻辑。