GPU Gems 1 · Chapter 17. Ambient Occlusion
用物体空间预计算与屏幕空间深度采样估计环境光可达性,增强接触、缝隙和褶皱中的明暗线索。
学习目标
- 能把 ambient occlusion 解释为表面点对环境光的可达性估计,并区分它与直接光照、阴影的职责
- 能从法线半球采样推导 object-space AO 的 occlusion factor,并说明预计算纹理或顶点属性的适用边界
- 能实现 screen-space AO 的 depth 读取、view-space reconstruction、邻域比较与 bias,并识别屏幕空间缺失
- 能在 radius、sample count、bias、分辨率和动态稳定性之间做出可解释的质量取舍
GPU Gems 1 第 17 章讨论 Ambient Occlusion:它不追求求解完整的全局光照,而是估计一个表面点周围的环境光有多少能够到达。缝隙、接触面和褶皱通常挡住更多方向,因此会自然变暗;这个额外的接触线索能让只靠 diffuse 的模型不再显得漂浮。
↡表示一个表面点能接收到的环境光比例的标量,通常取值为零到一 的输出可以记为 A(p):A(p) 越接近 0,说明 p 周围的半球越封闭;越接近 1,说明环境光更容易从各方向到达。常见做法是让 ambient 项乘以 A(p),或只让它调制 diffuse 中的环境部分,而不是覆盖整个最终颜色。
1. 从可达半球得到一个标量
对表面点 p,法线 n 定义了朝向外侧的半球。向半球内发射 N 条射线,统计没有命中几何体的方向,就能得到一个可解释的近似:
↡以表面法线为轴,在可见半球内选取方向并测试射线是否命中几何体的过程 的采样分布影响质量:均匀随机方向容易有噪声,分层、低差异序列或预计算方向集更容易获得稳定的屏幕结果。采样半径也必须和模型尺度或当前像素的 view-space 深度一致,否则同一套参数在近景和远景会产生不同的接触范围。
上图中命中凸起的射线是 blocked,没有命中的射线是 open。这个定义使 AO 具有单调性:附近几何越多,A(p) 越低;但它仍是局部可见性统计,不能告诉我们遮挡物材质、光源颜色或多次反射的结果。
2. object-space AO:把昂贵的 ray test 放到预处理
↡在模型或场景坐标中对几何体执行半球可见性测试,并将结果预计算到纹理、顶点或其他资产数据中的方法 直接在物体空间为每个顶点、纹理 texel 或采样点发射射线。将未遮挡比例烘焙后,可以把它存成 AO texture 或顶点属性,在实时 shading 中低成本读取。
一个面向静态资产的预处理流程如下:
- 为采样点构造以 n 为轴的切线空间半球,并把每个方向变换到 object space。
- 对每个方向执行 ray intersection;命中模型内部或邻近几何时,把该方向记为 blocked。
- 对 open 数量归一化,按 UV texel 或顶点写入 A(p),并用 dilation 修补栅格边缘的空洞。
- 在材质中将 AO 乘到环境项;需要动画时,确认烘焙结果与蒙皮、形变和相邻物体仍然成立。
它能看到屏幕外的几何,结果也不会随相机移动而闪烁,适合静态建筑、道具和不变拓扑。代价是资产更新需要重新烘焙;动态遮挡物、可变形褶皱和灯光方向变化不会被这张静态图自动反映。AO 贴图的 UV 缝、分辨率和颜色空间也要纳入内容管线,否则接缝会比它解决的接触问题更显眼。
3. screen-space AO:从深度重建邻域
screen-space 方法把计算移到每帧的屏幕像素。首先从 depth buffer 读取当前像素的深度,再用投影矩阵的逆变换得到 view-space 点;法线可以来自 G-buffer,也可以从相邻 view-space 点的叉积近似。随后沿法线半球选择屏幕邻域样本,将样本位置投影回屏幕,读取样本深度并与预期深度比较。
vec3 p = reconstructViewPosition(uv, depth);
vec3 n = decodeOrReconstructNormal(uv);
float blocked = 0.0;
for (int i = 0; i < SAMPLE_COUNT; ++i) {
vec3 q = p + tangentHemisphere(i, n) * radius;
vec2 sampleUv = projectToScreen(q);
float zSample = texture(depthTex, sampleUv).r;
float delta = viewDepth(zSample) - q.z;
blocked += step(bias, delta) * validScreenSample(sampleUv);
}
float ao = 1.0 - blocked / float(SAMPLE_COUNT);
ambient *= ao;↡从屏幕深度和逆投影矩阵恢复当前像素在相机坐标中的位置与深度关系的步骤 的关键是统一深度约定:投影后的非线性 depth 不能直接与线性 view-space z 相减。样本还要检查屏幕边界、前后方向与深度范围;否则边界的背景值会被当成无遮挡,产生亮边。
screen-space AO 的优势是适应动态物体、无需离线烘焙,并且可以在后处理或 deferred pipeline 中复用已有 depth。它的边界也很具体:屏幕外的墙不会被采到,被前景遮住的遮挡物不会进入 buffer,低分辨率 depth 会把细缝量化掉,相机移动时固定的随机旋转还可能产生闪烁。发布前应分别测试镜头运动、角色动画、屏幕边缘和物体尺度变化。
4. 参数与伪影:把质量预算写出来
↡用于避免深度精度或浮点误差把接收表面误判为自身遮挡的深度比较偏移量 只应略大于重建误差。radius 决定遮挡影响的空间范围:小半径能保留接触细节,大半径会把不同物体之间的距离也混进来。sample count 决定统计方差;少量样本便宜但容易噪声,多样本更稳却增加纹理读取和 ALU 成本。
常见伪影可以按原因排查:
- acne:bias 太小、深度精度不足或法线重建有误;先验证线性深度和法线方向。
- halo:radius 太大,或者深度边界没有做 thickness、normal 或邻域一致性判断;减小半径并把接触与远距离遮挡分开。
- 漏光:遮挡物在屏幕外、被前景隐藏或 bias 太大;物体空间 AO、额外深度层或混合方案可以补足它。
- 噪声与闪烁:样本太少或每帧随机旋转没有时序处理;使用低差异方向、旋转噪声的时序重投影和适度 blur,但要防止把 contact cue 抹平。
AO 最好作为小幅度的环境项调制,并保留 artist 可控的强度和距离衰减。若把 AO 强度推到纯黑来“修”建模问题,最终会把接触线索变成不自然的污渍;可解释的半径、bias 和强度更容易随相机、分辨率和平台变化进行校准。
5. 动手实验:先预测,再调参数
先预测三个变化:把 radius 拉大,接触阴影会扩大但 halo 风险也会上升;把 bias 拉大,acne 会减少但近距离遮挡会漏掉;把 samples 降到很低,静态画面会出现颗粒或带状变化。再切换 object-space 和 screen-space,观察屏幕外遮挡物为何只能在前者中保留。
Ambient Occlusion Lab
先猜:radius 变大,contact cue 与 halo 会怎样?
观察:radius 提升接触范围也提升 halo 风险;bias 可减少 self-occlusion acne,却会增加漏光;samples 越少越容易有噪声。screen-space 还会受屏幕外遮挡物缺失影响。
这个实验用确定性的示意数据表达参数关系,不是替代 GPU profile 的真实性能测量。它把一个发布检查拆成四项:接触范围是否可信,深度比较是否稳定,屏幕边缘是否出现 halo,以及在目标帧预算内是否有足够的 samples。
小结:用局部可见性补足接触线索
- ambient occlusion 估计半球环境光的可达性,A(p) 越小表示附近几何越封闭。
- object-space AO 可以预计算并看到屏幕外几何,适合静态或拓扑稳定的资产,但动态遮挡不会自动更新。
- screen-space AO 从 depth buffer 重建 view-space 邻域,适合动态场景,却受屏幕外、深度分辨率和边界伪影限制。
- radius、sample count 与 depth bias 必须一起调;提升其中一个维度不能自动修复另一个维度的错误。
- AO 应以可解释的小幅度调制 ambient 或 diffuse,而不是代替直接光、阴影或完整全局光照。
练习
问题 1|改 Demo 代码 把示例 shader 的 radius 从 0.25 改为 0.65,并保持 sample count 与 bias 不变。请预测接触区域、远距离表面和屏幕边缘分别会发生什么变化。
问题 2|推导题 某点有 32 条半球样本,其中 8 条命中几何体。若基础环境光为 1.0,按 A(p) = N_open / N 调制 ambient,忽略其他项时得到什么 AO 因子和环境亮度?
问题 3|独立实现题 为一个会移动的角色选择 object-space、screen-space 或混合方案。你的目标是保留薄缝接触、避免镜头运动闪烁,并说明至少一个无法由单一 screen-space buffer 解决的情况。
名词解释
本章出现的专业名词,用大白话再讲一遍。
- ambient occlusion
- 表面点接收环境光的可达性估计,通常以零到一的标量表示。
- hemisphere sampling
- 以表面法线为轴选择半球方向并测试射线可见性的过程。
- object-space ambient occlusion
- 在物体空间预计算半球可见性并存入纹理或顶点数据的方法。
- view-space reconstruction
- 从屏幕深度和逆投影关系恢复相机坐标位置的步骤。
- depth bias
- 用于避免深度比较误判表面自身遮挡的偏移量。