每日编程实践: Ambient Occlusion(环境光遮蔽)
Ambient Occlusion(环境光遮蔽)
环境光遮蔽(AO)是一种近似全局光照效果的技术,通过估算每个表面点被周围几何体遮蔽的程度,模拟出接触阴影、角落变暗等真实感效果。
项目目标
- 实现基于蒙特卡洛积分的 AO 渲染器
- 在半球方向随机采样光线,统计遮蔽率
- 构建包含多个球体和围合墙面的 Cornell Box 场景
- 量化验证 AO 梯度(角落暗、开阔区域亮)
效果展示

场景细节:
- 🔵 中央大球:球顶最亮(无遮蔽),底部接地点最暗(地面遮蔽)
- 🔵 角落小球:被墙角和地面三面遮蔽,明显偏暗
- 🟫 地面:球体正下方出现接触阴影,物理正确
核心原理
蒙特卡洛积分
AO 的数学定义是对法线半球上的可见性函数积分:
1 | AO(p) = (1/π) ∫_Ω V(p, ω) · (ω · N) dω |
其中 V(p, ω) 是可见性函数(1=未遮蔽,0=遮蔽),用蒙特卡洛方法估算:
1 | double computeAO(const Vec3& p, const Vec3& N, const Scene& scene, |
余弦加权重要性采样
相比均匀半球采样,余弦加权采样让更多光线集中在法线方向附近(贡献最大的区域),减少噪声、加速收敛:
1 | Vec3 cosineSampleHemisphere() { |
TBN 矩阵(Gram-Schmidt 正交化)
将采样方向从局部坐标(Z 轴 = 法线)变换到世界坐标:
1 | void buildTBN(const Vec3& N, Vec3& T, Vec3& B) { |
量化验证
程序内置量化验证,检查 AO 梯度是否物理正确:
| 测试点 | AO 值 | 像素亮度 | 预期 |
|---|---|---|---|
| 球顶部 | 1.00 | 246 | 最亮 ✅ |
| 角落(墙角地面) | 0.30 | 222 | 较暗 ✅ |
| 球底接地点 | 约0 | 145 | 最暗 ✅ |
亮度梯度:球顶(246) > 角落(222) > 球底接触(145),物理规律正确!
技术总结
今天掌握的技术点:
- 蒙特卡洛 AO 积分:随机采样估算遮蔽率,样本数越多噪声越小
- 余弦加权采样:PDF = cosθ/π,与被积函数形状匹配,比均匀采样高效
- TBN 矩阵构造:Gram-Schmidt 正交化是最稳健的方法,避免法线平行奇异
- 自交偏移(Bias):光线起点沿法线偏移 1e-4,解决浮点精度引起的自遮蔽
- 最大遮蔽距离:限制 AO 光线的最大距离,模拟局部光照(而非全局)
与 Phong/PBR 的区别:
- Phong/PBR 描述直接光照(光源→表面→眼睛)
- AO 描述间接光照的遮蔽(环境光被周围几何体阻挡)
- 现代渲染管线通常将 AO 叠加到直接光照上,得到更真实的效果
代码仓库
GitHub: https://github.com/chiuhoukazusa/daily-coding-practice/tree/main/2026/02/02-28-ambient-occlusion
完成时间: 2026-02-28 05:35
迭代次数: 1 次(一次编译通过)
运行时间: ~15 秒(64 samples/pixel, 800×600, 2×2 SSAA)
编译器: g++ 12.x (C++17, -O2)
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Chiuhou 技术博客!










