SIGGRAPH 2016 · ATVI-TR-16-01

Ground-Truth
Ambient Occlusion

Jimenez · Wu · Pesce · Jarabo — Activision Blizzard / U. Zaragoza

环境光遮蔽的核心问题

环境光遮蔽(Ambient Occlusion,AO)是实时渲染中近似全局光照最常用的手段之一。它描述了一个着色点 x 从均匀环境光中接收到的可见性积分:当周围几何体遮挡了来自某些方向的光线时,该点会变暗。

▸ 核心积分
AO 本质上是在半球面上对可见性函数 V(x, ωᵢ) 加权 cos(θ) 的积分。无解析解,只能数值近似。

现有方法(SSAO、HBAO 等)普遍存在以下问题:

GTAO 的三大贡献

① GTAO
用视角方向重新参数化水平线积分,内积可解析求解,精确匹配光线追踪 Ground Truth
② 近场 GI 补偿
基于 AO 值和反照率拟合三次多项式,恢复多次弹射损失的能量
③ GTSO
将 AO 推广至镜面材质,用可见锥与 BRDF 叶瓣的交集预计算镜面遮蔽 LUT

GTAO 公式推导

GTAO 基于 Bavoil 等人的 HBAO 框架,但做了关键改造。理解推导需要掌握三个层次:

标准 AO 将反射辐射近似为均匀环境光 Lᵢ 乘以遮蔽积分:

EQ.2
// V = 二值可见性函数:1=可见,0=被遮挡(半径 r 内)
// 旧方法用连续衰减函数替代 V → "Obscurance"(有偏)
▸ 旧方法的问题
使用衰减函数(如 )是为了减少 overdarkening 的 ad-hoc 处理,没有物理依据,且破坏了积分的解析性。GTAO 坚持使用二值可见性,通过独立的 GI 补偿项(§5)来处理 overdarkening。

Bavoil (HBAO) 把积分域从半球面转化为一组切线方向 参数化的水平线(Horizon):

EQ.3
// 对每个切线方向 φ,搜索两侧最大地平线角 θ₁, θ₂
// 外积对 φ 做 Monte Carlo,内积对 θ 积分,仍需 LUT

HBAO 的内积仍使用数值积分或 LUT(Timonen),且参考系以法线为轴。

GTAO 的关键改变:将积分参考系从法线轴改为视角方向 为轴,并引入 (法线投影与视线夹角):

EQ.5 — GTAO 核心积分
// γ = arccos(⟨n̄ₓ, ωₒ⟩),n̄ₓ 为法线在切平面上的投影
// 内积 â 现在有闭合解析式;外积对 φ 做 Monte Carlo
▸ 为什么换参考系就能解析?
为轴时,被积函数 是纯三角形式,其不定积分存在闭合形式。以法线 为轴时则需要额外的空间变换,破坏了三角函数的简洁结构。

对每个方向 ,内积 的解析结果(这是 GTAO 效率的核心来源):

EQ.7 — 解析内积
// 仅需 2×cos、1×sin 加 3×acos;Shader 完全 memory-bound,ALU 几乎零开销
EQ.8 — 最终形式(含法线投影修正)
// ‖n̄ₓ‖ = 法线投影到切平面后的模长,修正因参考系旋转引入的 dot-product 缩放

▸ 交互:解析内积 â 与 θ₁/θ₂ 的关系

解析值 â =   |   对应 AO =

实现细节:如何达到 0.5ms

GTAO 的目标是 PS4 1080p 60fps 的 0.5ms 预算。以下是各个优化层级,点击展开详情:

01
半分辨率计算 + 双边上采样
在 540p 计算 AO,双边滤波上采样至 1080p
半分辨率直接将 ALU 和带宽开销减半。上采样使用双边(bilateral)滤波,以深度/法线为 edge-stopping 权重,有效避免了边缘处的泄漏(bleeding)。由于 GTAO 本身是 memory-bound,半分辨率大幅减少了 cache miss。
02
空间积分:4×4 邻域双边重建
单像素仅采样 1 个方向,通过 4×4 邻域重建 16 个方向
对每个像素,只随机采样 1 个方向 φ 的水平线。然后使用 4×4 像素邻域的结果(用双边权重加权),等效于每像素拥有 16 个方向的样本。双边权重使用深度/法线差异来避免跨几何边界的错误混合。
03
时间积分:6-帧旋转 + 指数累积 Buffer
跨 6 帧交替使用不同旋转角,配合运动矢量重投影
将采样方向的旋转分布在 6 帧上(每帧用不同的旋转偏移)。历史帧结果通过运动矢量重投影(reprojection)对齐,使用指数累积 Buffer(EMA,Exponential Moving Average)混合。最终等效样本数:4 × 4 × 6 = 96 有效方向/像素
result = lerp(history_reprojected, current, α)
// α ≈ 0.1–0.2,静止时平滑收敛
04
厚度启发式(Thickness Heuristic)
纠正薄遮挡体(树叶、栅栏)在屏幕空间的过度遮蔽

深度缓冲无法表达几何体的厚度。薄物体(叶片、树枝)在 SSAO 中会产生不真实的大量遮蔽。

核心假设: 一个物体的厚度约等于它在屏幕空间的尺寸。在搜索水平角时,如果下一个深度样本比当前更近(cos 减小),说明可能是薄遮挡体的背面,此时用 EMA 混合而非直接取 max:

if cos(θₛ) ≥ cos(θₛ₋₁): θ = max(θₛ, θ) // 正常遮挡
else: θ = blend(θₛ₋₁, θₛ) // 可能是薄体背面
05
远场衰减(Far-Field Attenuation)
平滑截断以避免采样半径边缘的突变跳变
由于搜索半径有限,远处遮挡体的水平线突然"消失"会产生硬边。GTAO 在一个较大距离到最大搜索半径之间线性混合 AO 至 0。在实际使用中,远场遮蔽通常已经 bake 进 irradiance map,因此这个衰减也保证了运行时 AO 与 baked GI 的无缝融合。
▸ 性能数据
0.5ms / PS4 / 1080p 96 有效样本/像素 1 方向/像素/帧 Memory-Bound Shader

ALU 消耗极低(整个 AO 的热点在 texture fetch),GCN rsqrt 指令处理水平线搜索的 sqrt 运算。

近场间接光补偿

标准 AO 假设所有遮挡体都是吸收体(不反射任何光)。但真实世界中,相邻表面会将光线弹射回来。这导致 AO 在墙角、裂缝处过度变暗

▸ 关键观察
在反照率均匀的区域,某点的近场多次弹射间接光与其 AO 值之间存在强烈的数值对应关系。可以用 AO 和反照率拟合出这个映射,无需真正计算光线传输。

数据驱动拟合

作者在 7 种场景(包含各类遮蔽类型)中,用 7 个反照率值(0.1–0.9)渲染了 AO 值与 3-bounce 路径追踪 GI 的映射关系(见 Fig.6),发现三次多项式拟合效果极好,且系数与反照率呈线性关系:

EQ.10 — GI 补偿函数
// ρ ∈ [0,1] 表面反照率;A ∈ [0,1] AO 值
// 最终着色:Lᵣ = Lᵢ · (ρ/π) · G(A, ρ)

▸ 交互:GI 补偿函数 G(A, ρ) 可视化

曲线图 G(A) 随反照率变化
颜色感知对比(墙角截面)
纯 AO:  →  G(A,ρ) =   
EQ.4 — 完整着色
// F = Fresnel 项   G = GI 补偿漫反射   S = 镜面遮蔽(§6)

GTSO:镜面遮蔽

传统 Split-Integral 方案(UE4 等使用)假设可见性 ,完全忽略了镜面反射的遮蔽。GTSO 将可见性引入 BRDF 积分:

EQ.13–14 — 镜面遮蔽分解
// S 以 BRDF 为权重,方向相关(不同于 AO 的余弦加权)
// 归一化因子 Cᵥ = ∫ fᵣ⟨n,ωᵢ⟩dωᵢ = 恰好与 F 项约掉

计算方式:锥—锥相交

将两个难以精确积分的量都近似为锥体(Cone):

可见性锥 ΔV
方向 = bent normal b
锥角 αᵥ: cos(αᵥ) = √(1−Â)
镜面 BRDF 叶瓣锥 Ωₛ
方向 = 反射向量 ωᵣ
锥角 αₛ: cos(αₛ) = exp(−2.3 r²)
▸ 预计算 LUT
S(αᵥ, β, r, θₒ) 是一个四维函数,可以预计算为 32⁴ BC4 LUT(约 1MB)。若令 θₒ ≈ β(normal ≈ bent normal),可降为 32³ 的三维 LUT,误差很小。

▸ 交互:GTSO 参数可视化

αᵥ = °   αₛ = °   S ≈

实时 AO 积分演示

下面模拟 GTAO 在单像素上的水平线搜索过程。调整参数观察地平角、内积 â、以及最终 AO 值的变化:

▸ 单像素水平线搜索(截面视图)

θ₁ =
θ₂ =
â =
AO ≈

方法横向对比

方法 内积 可见性 GI 补偿 镜面 性能
SSAO 球面采样 深度比较
HBAO 数值水平线 衰减函数
HBAO+ LUT 水平线 衰减函数
GTAO 解析水平线 二值(物理正确) ✓ 多项式 ✓ GTSO LUT 0.5ms/1080p

Unity URP 实现要点

▸ 移植到 Unity URP 的关键点

Unity 从 URP 14(Unity 2022 LTS)起在 SSAO 中引入了 GTAO 模式。若自行实现需注意:

  • ① 水平线搜索步长需在视空间(View Space)执行,使用 Linear Eye Depth
  • ② bent normal 需要在采样后存入 G-Buffer(RG10B10A2 足够)供 GTSO 使用
  • ③ 时间 AA 的历史重投影可复用 TAA 的 velocity buffer
  • ④ GI 补偿系数(Eq.10)只有 6 个浮点数,直接硬编码进 shader 即可
  • ⑤ DrawMeshInstancedIndirect 场景中 per-instance 无法轻易获取 bent normal,建议先存入深度/法线 prepass