BRDF与微表面模型
全库目录221
本文目录16
一个粗糙度参数改变了高光宽度,也可能改变总反射能量。要解释这个结果,先看入射光、表面分布、Fresnel与几何遮蔽怎样组成BRDF。模型各项单独可视化以后,才能判断异常来自参数映射、能量分配还是光源单位。
反射积分
辐射量与单位
- Radiant Flux:单位时间传输的总辐射能量,单位 W。
- Irradiance:单位面积接收到的通量,单位 W/m²;它已经对入射方向积分。
- Radiance:沿某方向、单位投影面积、单位立体角传输的通量,单位 W/(m²·sr)。渲染方程主要用它。
在工程中经常统称“光强”或“亮度”,但推导和单位换算时不能混用。
Rendering Equation
表面点沿方向 的出射 Radiance:
- :自发光;
- :入射 Radiance;
- :BRDF;
- :入射方向对表面投影面积的影响;
- :表面法线上方半球。
实时渲染很少直接求完整积分。点光源、方向光等离散光源把部分积分变成少量求和;IBL、Lightmap、Probe 和光追则用不同方式近似剩余部分。
表面反射模型
Lambert Diffuse
理想漫反射向所有出射方向分布相同。BRDF 为:
是表面反照率。 用来满足能量守恒:对半球积分后,总反射能量不会超过入射能量。
实时 Shader 中常看到 albedo * saturate(dot(N,L)),省略的 可能被光源单位、引擎约定或美术标定吸收。不能看到代码少了 就直接判断错误,要先确认整套光照单位。
Phong 和 Blinn-Phong
Phong 使用反射方向 与视线 :
Blinn-Phong 使用半角向量:
指数 越大,高光越集中。它们便宜直观,但参数不直接对应稳定的物理材质,能量也需要额外归一化。
物理约束
BRDF 的约束
一个物理上合理的 BRDF 通常关心:
非负
反射不能凭空产生负能量。
能量守恒
所有方向反射出去的能量不能超过收到的能量。
Helmholtz Reciprocity
交换入射和出射方向,BRDF 应保持一致:
这是很多常规反射模型的约束,但带有特殊传输、非互易介质或某些艺术模型时需要重新确认。
微表面分解
微表面模型
微表面模型把粗糙表面看成许多微小镜面。宏观高光由三部分组成:
D:Normal Distribution Function
描述半角方向附近的微表面面积密度。它满足 ,因此可用于采样的密度是 ;单独的 不是归一化概率密度。
各向同性 GGX 使用
实时 PBR 常用 GGX/Trowbridge-Reitz,因为它的长尾高光比较符合真实粗糙表面。
Roughness 改变分布宽度。Roughness 越低,分布越集中,高光更尖。
F:Fresnel
描述光到达微表面后有多少被反射。Schlick 近似:
是正视角反射率。掠射角通常趋近更强反射。
G:Geometry Term
描述微表面之间的 Masking 和 Shadowing。一些面朝观察者的微表面可能被其他微表面挡住,或收不到光。
本文采用可分离 Smith 近似 ,其中
它分别估计观察方向与入射方向可见的微面比例。高度相关 Smith 会联合考虑两种遮蔽,不能只替换函数名而沿用同一份积分表。分母中的 来自微面反射方向到宏观表面方向的测度和投影换算;它不是控制高光强度的经验项。
代码中的组合顺序
以下 HLSL 机制函数展开 GGX、可分离 Smith 与 Schlick。N、V、L 是同一空间的单位向量,后两者从表面指向观察者和光源。颜色已在线性空间,metallic、roughness、F0 和 baseColor 均在 。函数返回含入射余弦的响应,调用端乘光源贡献、可见性和距离衰减,不能再乘一次 NoL。
static const float PI = 3.14159265359;
float DistributionGGX(float NoH, float alpha)
{
float a2 = alpha * alpha;
float denominator = NoH * NoH * (a2 - 1.0) + 1.0;
return a2 / (PI * denominator * denominator);
}
float SmithG1(float cosine, float alpha)
{
float a2 = alpha * alpha;
return 2.0 * cosine /
(cosine + sqrt(a2 + (1.0 - a2) * cosine * cosine));
}
float3 FresnelSchlick(float VoH, float3 F0)
{
float f = pow(1.0 - VoH, 5.0);
return F0 + (1.0 - F0) * f;
}
float3 EvaluateDirectResponse(
float3 N, float3 V, float3 L,
float3 baseColor, float metallic, float roughness, float3 F0)
{
float NoL = dot(N, L);
float NoV = dot(N, V);
if (NoL <= 0.0 || NoV <= 0.0) return 0.0;
float3 H = normalize(V + L);
float NoH = saturate(dot(N, H));
float VoH = saturate(dot(V, H));
float alpha = max(roughness * roughness, 0.002);
float D = DistributionGGX(NoH, alpha);
float G = SmithG1(NoV, alpha) * SmithG1(NoL, alpha);
float3 F = FresnelSchlick(VoH, F0);
// 合并 BRDF 与 NoL,避免先除再乘 NoL。
float3 specularResponse = D * G * F / (4.0 * NoV);
float3 diffuseWeight = (1.0 - F) * (1.0 - metallic);
return diffuseWeight * baseColor * NoL / PI + specularResponse;
}两个方向都在上半球时,V+L 非零。alpha 下限把理想镜面替换为有限宽度波瓣,属于明确的数值近似;材质端与 IBL 烘焙端要采用相同映射。极小值计算需使用足够精度,不能直接假定半精度稳定。
这里的 是简化的漫反射衰减,不能凭这个逐方向权重断言整个组合严格守恒;半球能量与材料参数的关系见 PBR材质。
以N=V=L=(0,0,1)、roughness=1、metallic=1、F0=0.04作为单方向检查。H=N,alpha=1,D=1/π,两个G1均为1,F=0.04,含NoL的镜面响应为0.04/(4π)≈0.0031831。metallic=1使漫反射为零。把光转到背面,入口应直接返回零。
这个方向点值不是材料总反射率;白炉积分必须遍历整个半球。正面点值正确而白炉偏差很大时,优先检查角度变化、测度和样本权重,而不是把单点响应强行缩放到1。
参数与能量
粗糙度映射
不同 BRDF 对 Roughness 的映射不同。引擎还可能使用 让美术控制更线性。
跨引擎复制 Roughness 时要核对:
- 输入是 Roughness 还是 Smoothness;
- 是否平方;
- NDF 和 Geometry Term;
- IBL 预过滤的 Roughness 到 Mip 映射;
- 纹理是否经过正确颜色空间解码。
多次散射
单次微表面模型会把被 Masking/Shadowing 挡住的能量直接丢掉,粗糙材质可能显得过暗。多次散射补偿会估计这些能量在微表面间再次反射后的贡献。
实时引擎常用近似或查表,不会逐次追踪所有微表面反弹。
验证方法
- 使用灰球、金属球和不同 Roughness 阶梯图。
- 固定曝光和环境,避免自动曝光掩盖能量问题。
- 检查 、、D、F、G 的单独可视化。
- 对比直接光和 IBL 中的 Roughness 响应是否连续。
- 白炉测试(White Furnace Test)将所有入射方向设为单位辐射亮度,数值积分 。对纯反射、无吸收且包含多次散射的模型, 应为 1;单次微表面模型允许 ,不能把这种模型误差误判为程序错误。
本页的 数值检查 验证 NDF 投影面积归一化、交换光线与视线的互易性、背面返回零以及白炉能量。对于 、、,本文模型给出 ;这个解析值能识别余弦或分母被重复使用的问题。运行 python -B 知识库/examples/revision/test_lighting.py。
相关主题
示例与验证范围:代码按文中前提解释机制,完整类型、资源和项目状态需由接入工程补齐。完整目标引擎运行与GPU性能为UNVERIFIED;正文给出的机制与实际实验范围分别记录。
参考资料
- Bruce Walter et al., Microfacet Models for Refraction through Rough Surfaces.
- Brian Karis, Real Shading in Unreal Engine 4.
- Google Filament:Specular BRDF、Energy loss in specular reflectance,核对 GGX、Smith、Schlick 与白炉定义。
- LearnOpenGL,
src/6.pbr/1.1.lightingand1.2.lighting_textured.