每日编程实践: PBR Cook-Torrance BRDF 渲染器
PBR Cook-Torrance BRDF 渲染器
今天实现了基于物理的渲染(Physically Based Rendering, PBR)中最核心的 Cook-Torrance BRDF 模型,渲染出 5×4 材质球阵列,直观展示金属度(Metallic)和粗糙度(Roughness)对材质外观的影响。
项目目标
- 实现完整的 Cook-Torrance BRDF 光照模型
- 展示 PBR 的两个核心参数:金属度 × 粗糙度
- ACES 色调映射 + sRGB Gamma 校正
- 4 点光源 + 简单阴影
渲染结果

- 横轴(左→右):金属度 0.0 → 1.0
- 纵轴(上→下):粗糙度 0.05 → 1.0
- 材质颜色:金黄色(R:1.0, G:0.71, B:0.29)
理论基础
Cook-Torrance BRDF
PBR 核心公式:
1 | f(l,v) = kd · (albedo/π) + ks · (D·G·F) / (4·(n·v)·(n·l)) |
其中三个镜面项分别是:
D - 法线分布函数(GGX/Trowbridge-Reitz)
描述微表面法线的统计分布,GGX 相比 Blinn-Phong 有更真实的高光拖尾:
1 | double distributionGGX(double NdotH, double roughness) { |
G - 几何遮蔽函数(Smith’s + Schlick-GGX)
模拟微表面的自遮挡效果,粗糙表面自遮挡更严重:
1 | double geometrySmith(double NdotV, double NdotL, double roughness) { |
F - Fresnel 方程(Schlick 近似)
掠射角度反射更强,金属和非金属基础反射率不同:
1 | Vec3 fresnelSchlick(double cosTheta, const Vec3& F0) { |
金属度的物理意义
- 非金属(m=0):F0 = 0.04(4% 基础反射率),有漫反射(kD = 1 - kS)
- 金属(m=1):F0 = albedo(用材质颜色作为反射率),无漫反射(kD = 0)
核心实现
1 | Vec3 cookTorranceBRDF( |
量化验证
| 球体位置 | 金属度 | 粗糙度 | 中心色 RGB | 亮度 |
|---|---|---|---|---|
| 第1行-第1列 | 0.00 | 0.05 | RGB(202,181,119) | 167 |
| 第1行-第3列 | 0.50 | 0.05 | RGB(113,104,68) | 95 |
| 第1行-第5列 | 1.00 | 0.05 | RGB(45,35,23) | 34 |
| 第4行-第1列 | 0.00 | 1.00 | RGB(195,172,111) | 159 |
| 第4行-第5列 | 1.00 | 1.00 | RGB(123,102,56) | 93 |
物理规律验证:低粗糙度(0.05)金属球的中心亮度随金属度增大而显著降低(167→34),这是因为镜面高光极度集中,采样中心点不在高光处。高粗糙度(1.0)球的亮度变化更平缓(159→93),漫射光均匀分布。✅
技术总结
今天学到的关键点:
- PBR 的本质:统一的能量守恒框架,不是”看起来更好”,而是基于物理正确
- GGX vs Blinn-Phong:GGX 的高光拖尾更自然,不会突然截断
- kD 和 kS 的平衡:
kD = (1 - F) * (1 - metallic)确保能量守恒 - F0 的重要性:非金属统一用 0.04,金属用 albedo 颜色,这个简化非常优雅
- ACES 色调映射:必须的步骤,不做的话高光会过爆
遇到的设计决策:
- 阴影采样偏移
N * 0.001避免自相交 roughness = max(roughness, 0.05)避免除零(纯镜面在实践中也不存在)- 4 个光源使场景更有立体感
代码仓库
GitHub: https://github.com/chiuhoukazusa/daily-coding-practice/tree/main/2026/02/02-27-pbr-cook-torrance
完成时间: 2026-02-27 05:32
迭代次数: 1 次(一次编译成功)
渲染时间: ~0.2 秒(800×600,20 球体,4 光源)
编译器: g++ 12.3.1 (C++17)
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Chiuhou 技术博客!










