Unity Shader BRDF双向反射分布函数

深入理解能量守恒定律与微平面理论(Cook-Torrance 模型)------物理渲染的数学基础

01什么是 BRDF?

双向反射分布函数(Bidirectional Reflectance Distribution Function,BRDF) 是描述光线如何从不透明表面反射的核心数学工具。它量化了从某一入射方向 ωᵢ 照射到表面上,有多少能量会沿出射方向 ωₒ 反射出去。

BRDF 是现代**基于物理的渲染(Physically Based Rendering, PBR)**的核心,它让计算机生成图像达到了照片级真实感。

数学定义

BRDF 在数学上定义为出射辐射亮度(Radiance)对入射辐照度(Irradiance)的微分比值:

  • Lₒ(ωₒ)沿方向 ωₒ 的出射辐射亮度(W·sr⁻¹·m⁻²)
  • Lᵢ(ωᵢ)沿方向 ωᵢ 的入射辐射亮度(W·sr⁻¹·m⁻²)
  • θᵢ入射光方向与表面法线之间的夹角
  • dωᵢ入射方向对应的立体角微元(sr)

🔑 关键性质

BRDF 具有亥姆霍兹互易性(Helmholtz Reciprocity):交换入射和出射方向,值不变。即 f(ωᵢ, ωₒ) = f(ωₒ, ωᵢ)。这一性质保证了物理一致性。

02能量守恒:物理渲染的基石

能量守恒要求:反射出去的光能量总量不能超过入射的光能量。这是 BRDF 合法性的最基本物理约束,也是区分"物理正确"渲染与经验渲染的核心差异。

半球积分约束

将 BRDF 在整个上半球积分,得到方向半球反射率(Directional Hemispherical Reflectance),其值必须 ≤ 1:

⚠️ 为什么 Blinn-Phong 不满足能量守恒?

传统 Blinn-Phong 模型的高光项 (n·h)^s 没有归一化因子,当粗糙度降低(光泽度 s 增大)时,高光亮度会无限增加。Cook-Torrance 模型通过分母项 4(n·ωᵢ)(n·ωₒ) 确保了能量守恒。

03微平面理论:表面模型

**微平面理论(Microfacet Theory)**假设宏观表面由无数微小、光学平滑的"微面元(microfacet)"组成。每个微面元都是一个完美的镜面,有自己的法线方向。宏观表面的粗糙度由这些微面元法线的统计分布决定。

三大效应

🔬

法线分布(NDF)

统计描述微面元法线指向半向量 h 的概率密度。粗糙度越低,分布越集中,高光越尖锐。

🌑

自阴影(Shadowing)

微面元可能被相邻面元遮挡入射光(shadowing)或遮挡出射光(masking),产生几何衰减。

💡

菲涅耳效应(Fresnel)

光在表面与介质边界的反射率取决于入射角。掠射角(接近 90°)时反射率趋近 100%。

04Cook-Torrance BRDF 详解

Cook-Torrance 模型将 BRDF 分解为漫反射(Diffuse) 与**镜面反射(Specular)**两部分:

其中漫反射项为朗伯(Lambertian)模型,镜面反射项为微平面模型:

  • D(h)法线分布函数(Normal Distribution Function):描述微面元法线与半向量对齐的概率密度
  • F(ωᵢ, h)菲涅耳方程(Fresnel Equation):描述不同入射角下的反射率
  • G(ωᵢ, ωₒ)几何遮蔽函数(Geometry Function):描述微面元的自遮挡效应
  • 4(n·ωᵢ)(n·ωₒ)归一化因子:确保能量守恒的分母项

05三大核心函数

D --- GGX/Trowbridge-Reitz 法线分布

GGX 分布是目前工业界最广泛使用的 NDF,其"长尾"特性能产生真实的高光拖尾效果:

F --- Schlick 菲涅耳近似

菲涅耳方程描述了反射率随入射角变化的规律。Schlick 近似在保持高精度的同时大幅降低计算量:

G --- Smith 几何遮蔽函数

Smith 方法将阴影(shadowing)和遮蔽(masking)分开计算,再相乘。配合 GGX 使用 Schlick-GGX 近似:

06完整 GLSL 实现

以下是完整的 Cook-Torrance BRDF 在 GLSL(OpenGL/WebGL)中的实现:

cs 复制代码
// =============================================

// GGX 法线分布函数 (Normal Distribution)

// =============================================

float DistributionGGX(vec3 N, vec3 H, float roughness) {

    float a      = roughness * roughness;

    float a2     = a * a;

    float NdotH  = max(dot(N, H), 0.0);

    float NdotH2 = NdotH * NdotH;


    float denom  = NdotH2 * (a2 - 1.0) + 1.0;

    denom = PI * denom * denom;

    return a2 / denom;  // D_GGX

}


// =============================================

// Smith-Schlick 几何遮蔽函数

// =============================================

float GeometrySchlickGGX(float NdotV, float roughness) {

    float r = roughness + 1.0;

    float k = (r * r) / 8.0;  // 直接光照用此公式

    return NdotV / (NdotV * (1.0 - k) + k);

}


float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) {

    float NdotV = max(dot(N, V), 0.0);

    float NdotL = max(dot(N, L), 0.0);

    float ggx1  = GeometrySchlickGGX(NdotV, roughness);  // masking

    float ggx2  = GeometrySchlickGGX(NdotL, roughness);  // shadowing

    return ggx1 * ggx2;  // G_Smith

}


// =============================================

// Schlick 菲涅耳近似

// =============================================

vec3 FresnelSchlick(float cosTheta, vec3 F0) {

    return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);

}
cs 复制代码
// =============================================

// 完整 Cook-Torrance PBR 渲染

// =============================================

vec3 CookTorranceBRDF(

    vec3 N,           // 表面法线

    vec3 V,           // 视线方向

    vec3 L,           // 光源方向

    vec3 albedo,      // 漫反射颜色

    float metallic,   // 金属度 [0,1]

    float roughness   // 粗糙度 [0,1]

) {

    vec3 H = normalize(V + L);      // 半向量


    // ---- 菲涅耳基值 F0 ----

    // 绝缘体 F0 ≈ 0.04,金属 F0 = albedo

    vec3 F0 = mix(vec3(0.04), albedo, metallic);


    // ---- Cook-Torrance 镜面项 ----

    float NDF = DistributionGGX(N, H, roughness);  // D 项

    float G   = GeometrySmith(N, V, L, roughness);  // G 项

    vec3  F   = FresnelSchlick(max(dot(H, V), 0.0), F0); // F 项


    float NdotL = max(dot(N, L), 0.0);

    float NdotV = max(dot(N, V), 0.0);


    vec3 numerator   = NDF * G * F;

    float denominator = 4.0 * NdotV * NdotL + 0.0001; // 防除零

    vec3 specular    = numerator / denominator;


    // ---- 能量守恒拆分 ----

    // kS = 镜面比例 = F;kD = 漫反射比例 = 1 - kS

    vec3 kS = F;

    vec3 kD = (vec3(1.0) - kS) * (1.0 - metallic); // 金属无漫反射


    vec3 diffuse = kD * albedo / PI; // 朗伯漫反射


    // ---- 最终 BRDF 输出 ----

    return (diffuse + specular) * NdotL;

}

07参数对比与视觉效果

Cook-Torrance BRDF 主要受以下三个参数控制,其组合决定了材质的外观:

参数 范围 视觉效果 典型材质
roughness (粗糙度) 0.0 --- 1.0 0 = 镜面清晰高光;1 = 完全漫射 镜面/塑料/混凝土
metallic (金属度) 0.0 --- 1.0 0 = 绝缘体(白色高光);1 = 金属(彩色高光) 木材/铁/金
F0 (基础反射率) 0.02 --- 1.0 绝缘体 ≈ 0.04;半导体 ≈ 0.3;金属 ≈ albedo 玻璃/硅/铜
albedo (漫反射色) vec3 [0,1]³ 绝缘体的漫反射颜色;金属的镜面颜色 任意颜色

💡 PBR 工作流小结

实际 PBR 渲染管线中,roughness、metallic、albedo 通常以纹理贴图的形式输入,允许单个模型表面有复杂的材质变化。Cook-Torrance BRDF 结合 IBL(基于图像的光照)和实时阴影贴图,已成为当今游戏引擎(UE5、Unity HDRP)和离线渲染器(Arnold、Cycles)的标准材质模型。

相关推荐
十五年专注C++开发2 小时前
Cocos2d - x: 一款开源跨平台 2D 游戏框架
运维·c++·游戏·开源·游戏引擎·cocos2d
魔士于安16 小时前
unity完整项目走廊
游戏·unity·游戏引擎·贴图·模型
程序员正茂18 小时前
在Unity3d2021.3.35中实现MQTT异步客户端
mqtt·unity·异步
海海不瞌睡(捏捏王子)20 小时前
Unity YAML
unity·游戏引擎
海海不瞌睡(捏捏王子)1 天前
Unity A*寻路算法
算法·unity
weixin_423995001 天前
unity 虚拟数字人-接讯飞虚拟人
unity·游戏引擎
小贺儿开发1 天前
Unity3D 家居视频遥控效果演示
unity·udp·人机交互·网络通信·winform·远程·photon
mxwin1 天前
Unity URP 阴影映射 深度纹理、阴影采样与分辨率控制的深度解析
unity·游戏引擎·shader·着色器
YY_pdd1 天前
godot的项目打包为安卓程序
游戏引擎·godot