Base-时域抗锯齿 TAA
Temporal Anti-Aliasing(TAA,时域抗锯齿)是当前游戏中最主流的抗锯齿方案。它不直接增加采样数,而是把过去若干帧的样本沿时间轴累积,以极低的每帧开销实现接近超采样的抗锯齿效果。
但它并非无代价——时域复用带来了 Ghosting、Bleeding、Smearing 等一系列 artifact。理解这些问题的根源和对应的缓解方法,比背诵 TAA 的步骤更重要。
1. 为什么要用时间换采样
1.1 传统抗锯齿的局限
- SSAA(超级采样):每个像素做 N 次采样再平均。效果好,但开销翻 N 倍,移动端和 VR 不可接受。
- MSAA(多重采样):只在三角形边缘增加采样,对 Shading Aliasing(高光闪烁、Shading 噪声)无效。
- FXAA(快速近似):后处理模糊边缘,会导致画面模糊、细节损失。
- SMAA(子像素增强):比 FXAA 聪明但依然基于单帧信息,对 Shading Aliasing 无能为力。
TAA 的出发点很简单:GPU 每帧都在做各种各样的采样(光照、阴影、AO),把这些样本跨帧累积起来,等效于在时间维度上做了超采样。 每帧只需渲染一帧,但累积 N 帧后的有效样本数 ≈ N 倍。
1.2 核心思路
1 | |
通过 Jitter 让采样点随时间错开,再通过累积覆盖更多采样位置。N 帧累积后等效于 N× SSAA 的采样密度,但每帧只做一次着色。
2. 核心流程拆解
2.1 亚像素 Jitter
在每帧渲染前,对投影矩阵施加一个亚像素级的偏移,让采样网格在子像素位置上每帧错开。
1 | |
Jitter 序列通常使用 Halton 序列(一种低差异序列,保证采样点覆盖均匀,避免周期性图案):
1 | |
每帧使用 (Halton2(frameIndex), Halton3(frameIndex)) 作为 Jitter 偏移。
注意:Jitter 改变的是采样位置,不是最终像素的显示位置。输出颜色时不需要”取消 Jitter”,TAA 累积的结果已经对应正确像素位置。
2.2 Motion Vector(运动向量)
TAA 要混合历史帧,必须知道”上一帧的像素 p 对应到当前帧的哪个位置”。这个对应关系由 Motion Vector 提供。
Motion Vector 的核心逻辑:
1 | |
生成方式:
- 相机运动:直接根据 View-Projection Matrix 前后变换计算
- 物体运动:Vertex Shader 输出上一帧和当前帧的裁剪坐标,Pixel Shader 中做差
- 引擎封装:Unity 的
_CameraMotionVectorsTexture、UE4 的GBuffer Velocity都是预生成的 Motion Vector RT
没有 Motion Vector 的区域(如粒子、透明度混合物体),TAA 只能靠颜色相近度来混合,效果受限。
2.3 历史帧累积与混合
拿到 History Buffer 和 Motion Vector 后,对每个像素:
1 | |
混合权重受多种因素调节:场景变化速度、运动速度、曝光变化等。
3. 核心问题与解决方案
TAA 的难点不在正向流程,而在处理时域复用带来的各种 artifact。如果只是简单混合历史帧,结果会充满拖影和鬼影。
3.1 Ghosting(拖影 / 鬼影)
现象:运动物体后面拖着彩色尾巴,或者物体移动后原来的位置残留旧颜色。
原因:历史帧中包含了物体移动前的位置的颜色,但当前帧中物体已经移走,简单混合会把旧位置的颜色带到新位置。
解决方案:
方案一:History Clipping / Clamping(最核心、最常见)
对当前帧像素的一定邻域(通常是 3×3)计算颜色的 AABB(轴对齐包围盒) 或更紧致的凸包,然后将历史颜色向这个包围盒做 clip/clamp,强制历史颜色落在当前帧”合理”的颜色范围内。
1 | |
如果历史颜色中的某个通道明显超出当前帧邻域的范围,说明那个像素包含了一个已经不存在的物体信息,直接 clip 掉。
方案二:Variance Clipping(更紧致的裁剪)
AABB clipping 在色彩范围大时仍然不够紧。Variance Clipping 进一步缩小允许的范围:
1 | |
γ(gamma)通常取 1.0~3.0。γ 越小裁剪越激进、Ghosting 越少,但可能造成闪烁。
在哪个颜色空间做 clip 很重要:在 YCoCg 空间做 clip 比 RGB 空间效果更好,因为 YCoCg 去除了亮度与色度的耦合,能让 clipping 在不同通道间更独立。
3.2 Bleeding(颜色渗漏)
现象:前景物体边缘沾染了背景的颜色,或者背景颜色”渗入”了前景物体边缘。
原因:在物体边缘处,Motion Vector 可能不正确,导致采样历史帧时取到了前景/背景的混合值,或者重投影后的位置落入了另一个物体。
解决方案:
利用深度进行颜色排斥:在采样历史帧时,对比当前像素与历史采样点的深度。如果深度差异超过阈值,说明重投影到了错误的物体上,降低历史权重或完全放弃历史。
1 | |
配合 Neighborhood Clamping:上述的 AABB / Variance Clipping 在边缘区域同样能自动抑制颜色渗漏,因为邻域内的颜色范围限制了历史颜色的极端值。
3.3 Smearing(涂抹感 / 细节丢失)
现象:动态物体表面细节模糊,像被”涂抹”了一层,尤其在快速运动时更明显。
原因:Motion Vector 精度有限,历史帧重投影不能完美对齐。加上 TAA 默认使用长时间累积(低混合率),运动中的细节会在时间维度上被平均化。
解决方案:
提高运动物体的混合速率:根据 Motion Vector 长度动态调整 blendFactor。
1 | |
锐化 Pass:TAA 之后通常跟一个后处理锐化(如 Adaptive Sharpen),补偿被时域平均模糊掉的高频细节。这部分锐化不是可选而是推荐——绝大多数实际部署的 TAA 都搭配了锐化。
反馈 Feedback 调整:在混合时考虑当前帧与历史帧的差异程度,差异大时降低历史权重。
3.4 Disocclusion(新暴露区域)
现象:相机运动后,原本被遮挡的区域暴露出新像素,这些像素没有有效的历史——历史帧对应位置是另一个物体。直接混合会导致鬼影。
原因:没有有效的历史帧信息。
解决方案:
减小历史权重:检测到 disocclusion 时,大幅降低甚至归零 blendFactor,完全信任当前帧。
检测方法:
- Motion Vector 不连续:当前像素的 Motion Vector 与周围像素差异过大,说明处于遮挡边界
- 深度不连续:当前像素深度与历史采样点深度差异大
- Sub-pixel Movement 过大:Motion Vector 的亚像素部分过大,说明重投影精度不够
1 | |
引入干净帧(Clean Frame):每 N 帧存储一份不应用 Jitter 的”干净”渲染结果,专门用于 disoccluded 区域的初始化。代价是需要额外渲染一次。
3.5 闪烁(Flickering)
现象:细小物体(铁丝网、草叶、远处高光)在高频闪烁。
原因:亚像素物体在 Jitter 不同偏移下每帧的覆盖情况剧烈变化,时域累积来不及稳定。
解决方案:
- 增加样本累积帧数(降低 blendFactor 或增加反馈延迟)
- 使用更稳定的 Jitter 序列(Halton 序列的优势在这里)
- 对细小物体做预滤波或降低其高频细节
- Temporal Upsampling(TAAU)方案中,通过更高分辨率的累积缓解
4. TAA 的典型实现要点
4.1 像素邻域采样
TAA 的质量很大程度上取决于邻域颜色范围的计算是否准确。常见做法:
- 使用 3×3 或 5×5 邻域
- 采样时用 点采样(point sampling) 而非双线性,避免引入额外混合
- 当前帧使用 4× 旋转棋盘采样(UE4 的做法)来减少带宽
4.2 YCoCg 颜色空间
RGB 空间各通道相关性高,clip 一个通道可能不当影响另一个。YCoCg 是一个去相关的颜色空间:
1 | |
在 YCoCg 空间做 clipping 和 variance 计算,比 RGB 更精确。
4.3 反馈机制
TAA 是一个反馈回路:当前帧的输出就是下一帧的 History。如果当前帧的混合或 clipping 有错误,会持续影响后续帧。
因此:
- 避免过于激进的 clipping:γ 太小或 AABB 范围太紧会引入闪烁,且闪烁会在帧间积累
- Feedback 补偿:可以根据帧间差异动态调整混合率
4.4 锐化
TAA 天然会引入模糊,几乎所有工程实现都跟随一个锐化 Pass:
- 简单方案:Unsharp Mask
- 方案:Adaptive Sharpening(FidelityFX CAS)
- 注意:锐化强度不应超过 TAA 带来的模糊,否则会放大 Jitter 产生的噪点
5. TAA 的变体与演进
TAAU(Temporal Anti-Aliasing Upsampling)
TAAU(UE5 的叫法)将 TAA 和上采样合并:在低分辨率渲染,利用 TAA 累积出高分辨率效果。核心逻辑与 TAA 相同,但额外处理:
- 低分辨率像素到高分辨率像素的分散(Distribute)
- 使用抖动序列控制像素的渲染位置
- 在高分辨率网格上累积样本
DLSS / FSR / XeSS
这些是 AI 辅助的 TAA 上采样方案:
- DLSS:用神经网络替代人工设计的 clipping/clamp,学习最优的历史帧融合
- FSR 2/3:基于 TAAU 框架,使用手工调优的时域累积 + 锐化
- XeSS:结合矩阵加速器做 NN 推理,原理类似 DLSS
它们的共同基础仍然是 TAA 的重投影 + 累积框架,只是把经验规则替换为了学习结果。
6. 总结
TAA 是一组以时域累积为核心的抗锯齿和后处理技术。它的核心不是某一公式,而是处理时域复用带来的 artifact 的技巧集合:
| 问题 | 根源 | 核心缓解手段 |
|---|---|---|
| Ghosting | 历史帧包含过时信息 | AABB / Variance Clipping |
| Bleeding | 重投影取到错误位置 | 深度排斥、Neighborhood Clamp |
| Smearing | 运动物体细节被平均 | 动态混合率、锐化 Pass |
| Disocclusion | 无有效历史 | 检测遮挡边界、归零权重 |
| Flickering | 亚像素覆盖不稳定 | 低差异序列、帧间稳定 |
理解 TAA 的关键不在于记住 Halton 序列怎么算,而在于理解:
- TAA 用时间换样本数 — 不增加单帧开销,累积多帧信息
- 所有 artifact 都来自过时的历史 — 解决方案都是让历史更快被淘汰
- AABB / Variance Clipping 是 TAA 的基石 — 几乎所有引擎的 TAA 核心都在做这件事
- TAA 是一个反馈系统 — 一帧的误差会持续影响后续帧,因此需要平衡抑制 ghosting 和避免引入新噪声
参考资料
- High Quality Temporal Supersampling(NVIDIA 2014) — 第一篇系统阐述 TAA 的论文,AABB Clipping 和 Variance Clipping 的原始出处
- Unreal Engine 4 Temporal Upsampling — TAAU 的工程实现描述
- FidelityFX TAAU(AMD) — AMD 的开源 TAAU 方案
- The Art of TAA(60fps Rendering 博客) — 对 TAA 常见问题与 trade-off 的深入讨论