屏幕空间后处理工程 2026:SSR/SSAO/TAA/DLSS 统一取舍
把屏幕空间反射、屏幕空间环境光遮蔽、时序抗锯齿与 AI 升采样器视为一个统一的“屏幕空间后处理层”,通过分层降级策略、把命中率与时序稳定性作为质量轴,把后处理开销从 8ms 压到 3ms 同时保画面干净。
约 40 分钟阅读11,731 字2 次阅读博主

把屏幕空间反射、屏幕空间环境光遮蔽、时序抗锯齿与 AI 升采样器视为一个统一的“屏幕空间后处理层”,通过分层降级策略、把命中率与时序稳定性作为质量轴,把后处理开销从 8ms 压到 3ms 同时保画面干净。

一句话摘要:把屏幕空间反射、屏幕空间环境光遮蔽、时序抗锯齿与 AI 升采样器视为一个统一的"屏幕空间后处理层",通过分层降级策略、把命中率与时序稳定性作为质量轴,把后处理开销从 8ms 压到 3ms 同时保画面干净。
任何做过主机/PC 端 AAA 项目的人,只要在 PIX 或 RenderDoc 里点开 GPU timing track,都会看到同一条经验曲线:剔除与几何阶段(2-3 ms) + 着色阶段(2-3 ms) + 后处理(2-5 ms) + UI 合成(0.5-1 ms),最后一段在 60 FPS 预算里常常吃掉 30%-50% 的 GPU 时间。这段开销里又至少有 70%-80% 集中在一组互相耦合的屏幕空间效果上:屏幕空间反射(SSR)、屏幕空间环境光遮蔽(SSAO/HBAO/GTAO)、时序抗锯齿(TAA)以及正在快速取代 TAA 的 AI 升采样器(TSR/DLSS/FSR/XeSS)。它们的共同特征是:都依赖 G-Buffer 派生出的深度与法线信息,都需要做跨像素的时域或空间邻域查询,都需要在"画面干净度"与"延迟/抖动/鬼影"之间做工程取舍。
把这四类效果拆开看,每一种都有独立的算法文献(SSR 的 Hi-Z tracing 自 2014 年 Bitachon 等人的工作起,SSAO 的 Crytek 原始提案可追溯到 2007 年,TAA 的 iterative accumulation 形式在 Unreal Engine 4 时代就已成主线,DLSS/FSR/XeSS/TSR 则是 2020 年之后才进入主流)。但工程团队真正苦的不是单个算法本身,而是它们放在一起时的耦合:SSR 的 resolve 阶段要复用 TAA 的抖动向量(jitter)以避免分帧采样产生锯齿;SSAO 的 half-res 策略要在 TAA 的 history buffer 里做跨帧的双线性插值;AI 升采样器的输入是带抖动的低分辨率颜色与 motion vector,输出又会被 SSR/SSAO 后续的接触阴影、半透明等效果依赖;任何一处配置错了都会引发连锁反应——SSR 在屏幕边缘突然出现色块、TAA 在角色转身时甩出长长的历史残影、FSR 在远景树丛上爆出网格状的瞬态噪声。
本文要做的事,就是把这四类屏幕空间效果作为一个统一的"屏幕空间后处理层"来分析,给出 2026 年主流引擎(UE5.6、Unity 6、Godot 4.6、自研引擎)实际在用的工程取舍,以及一份可以在两周内落地的工程清单。
把屏幕空间效果看成一个分层栈,2026 年主流引擎普遍采取如下五层抽象:
第一层,几何重建层(Geometry Reconstruction Layer)。它的职责是从 G-Buffer 的深度与法线出发,重建出屏幕空间曲面——具体包括:从 depth texture 派生出 view-space 法线(必要时通过 dFdx/dFdy 重建)、重建世界坐标(reconstruct world position from depth)、做曲面平滑(处理 depth 的阶梯状量化)、提供 Hi-Z pyramid 用于 SSR 的 early-out。
第二层,屏幕空间查询层(Screen-Space Query Layer)。这是 SSR 与 SSAO/HBAO/GTAO 的主战场:从屏幕空间出发的射线追踪(ray marching on Hi-Z)、cone tracing、sample gathering,本质上都是在"屏幕空间邻域"里做查询。
第三层,时域累积层(Temporal Accumulation Layer)。TAA 是这层的代表:把当前帧的颜色与 motion vector 引导到上一帧 history buffer,在加权平均后输出。AI 升采样器(TSR/DLSS/FSR 3.1+、XeSS)内部也实现了一个变种的时域累积——它们的 motion vector 输入格式、history rejection 逻辑,与 TAA 是同一类问题的不同特化。
第四层,空间重建层(Spatial Reconstruction Layer)。SSGI、bilateral upsampling、half-res resolve 的最后升采样,都依赖空间邻域内的去噪与边缘感知插值。这一层的算法从简单的 bilateral blur 进化到基于神经网络的端到端滤波(典型如 NVIDIA RTX SDK 的 NRD 库的 denoiser 模块)。
第五层,合成层(Composite Layer)。把屏幕空间效果与场景光照、半透明、UI、tone mapping 做最终合成。注意合成顺序直接影响画面观感——把 SSR 在 tone mapping 之前合成会得到物理上更准确的高光反射,但代价是 SSR 的曝光溢出时无法被后置 tone mapping 救回;反之亦然。
这三层之上的三个公共坐标系贯穿所有效果:NDC(view-space 归一化设备坐标,用于屏幕空间射线起点)、view-space(用于反射向量的反射矩阵计算)、world-space(用于命中点的几何判断)。三个坐标之间的转换在每个效果里都要做,2026 年的引擎普遍把这一组转换封装成 ScreenSpacePass 类的接口,接受一个 helper struct(PassParameters),里面包含 depth/normal/motion vector 的 SRV、各层 SRV/RTV 的 binding 表,以及一个 enum 表示本 pass 是 SSR/SSAO/TAA/Composite 中的哪一种。
五层抽象的好处是:每一层都可以独立地 profile、独立地 hot-reload、独立地 disable。比如 GPU crash dump 出来后,可以直接把"第四层空间重建"换成最简的双线性升采样定位 denoiser 是否是问题源;又比如在 PS5 上跑 AI 升采样器,只需替换第三层的实现,前两层不变。
屏幕空间反射(SSR)的算法骨架从 2014 年起就基本定型:从屏幕空间每个像素的可见点出发,沿着反射方向做射线行进,沿途查询深度金字塔(Hi-Z)做 early-out,命中后做交点判断并采样反射颜色。2026 年主流引擎在 SSR 上做的工程取舍,集中在三个维度:
第一,追踪步长与终止条件。Bitachon 等人的原始论文用固定步长,2026 年的实现几乎全部改成 adaptive step:步长与 Hi-Z 的当前 mip 等级成反比(mip 越深、像素覆盖的几何范围越大、步长越长),这样在最坏情况下(depth 极不连续的边缘)仍能保持稳定。同时引入 max distance(屏幕空间最大追踪像素数)和 thickness bias(避免薄墙被自己反射到自己),这两个参数直接决定 SSR 在哪类场景里"看起来对"。
第二,采样颜色与 resolve。命中点采样到的颜色往往不是最终反射颜色——尤其在抖动的低分辨率(half-res 或 quarter-res)SSR 下,采样点是屏幕空间整数像素,需要根据抖动向量做 sub-pixel offset 后再做一次采样。然后进入 resolve 阶段:把 SSR 结果与 fallback(环境贴图 cubemap 或 planar reflection)做加权混合,权重由"命中距离""屏幕边缘""粗糙度"等信号决定——离屏幕边缘越近、粗糙度越高,SSR 占比越低,fallback 占比越高。
第三,多层降级。这是 2026 年 SSR 工程师真正的日常:在一帧里同时维护三个 LOD——full-res SSR(高质量)、half-res SSR(中质量)、fallback-only(无 SSR)。Quality tier 自动按当前 GPU 占用率、玩家视距、动态分辨率(DLSS/FSR 输出)做切换。一个典型的工程配置:
enum class SsrQuality {
Off, // 关闭,纯 fallback
FallbackOnly, // 只用环境贴图
HalfRes, // half-res tracing + denoise
FullRes // full-res tracing + TAA-aware resolve
};
if (auto.getGpuBudgetMs() < 1.2f || isCinematicCamera()) {
quality = SsrQuality::HalfRes;
} else if (isHandheldPlatform()) {
quality = SsrQuality::FallbackOnly;
}
值得专门说的是 half-res SSR 的"history-aware resolve"。因为 SSR 在 half-res 下采样,边界处会出现锯齿;而 SSR 又依赖 TAA 的抖动向量做时域稳定——所以一个干净的实现是把 SSR 输出先升到 full-res,再让 TAA history buffer 在 SSR 输出上做加权平均,而不是 SSR 自己再做一次时域累积。两层时域滤波叠加会产生"双重残影",这是新手团队最常踩的坑之一。
Hi-Z pyramid 自身的内存开销也不可忽略:每多一帧的 Hi-Z 大约会多消耗 depth texture 1/3 的内存(按 mip 链 1+1/4+1/16+...≈1.33 倍)。在显存紧张的移动端,常见做法是复用上一帧的 Hi-Z 做本帧 SSR,而本帧渲染时再单独维护一个新的 Hi-Z——这样多消耗一份带宽换一份内存,在 TBDR 上是划算的。
SSAO 是屏幕空间效果里算法演进最快的——从 Crytek 2007 年的原始 SSAO(随机 sample kernel 在半球内采样),到 2011 年的人造 horizon-based AO(HBAO)、再到 2015 年的 GTAO(ground-truth AO,基于射线-球积分近似)。2026 年主流引擎默认基本都切到 GTAO 或类 GTAO(Strata、UE5 的 AO2 都是这个谱系)。但工程实现上的关键差异比算法本身更值得讨论。
第一,曲面法线重建。SSAO 的输入是 depth + normal,但深度缓冲存的是 quantized depth,从 quantized depth 派生出的 view-space normal 在远距离会出现明显抖动。一个被工程界反复验证的做法是:在 half-res normal 上做一次轻微的双边滤波(只在深度变化小的区域平滑,深度变化大的边缘保持锐利),再升采样到 full-res。这一步既给 SSAO 提供更稳定的曲面法线,也给后续的接触阴影、半透明等效果提供更可靠的几何输入。UE5.6 的 SSAO pass 默认就在这一步上做了约 0.3 ms 的优化。
第二,sample kernel 与噪声模式。GTAO 的核心是用少量射线(8-16 条)沿切平面方向做多次短距离射线追踪,再对每条射线的可见性做加权积分。这里的工程权衡是:kernel 数量与画面质量近似对数关系——从 8 条加到 16 条质量提升明显,从 16 条加到 32 条几乎看不出来,但开销线性增长。2026 年的一个优化是把 kernel 数量做成动态:屏幕中心 16 条、屏幕边缘 8 条(因为屏幕边缘 AO 贡献小),这样平均开销减半而画面观感几乎无差。
第三,half-res 与 bilaterial upsampling。SSAO 几乎所有引擎都跑在 half-res,因为它的算法特性对分辨率不敏感(结果主要是低频遮蔽)。但 half-res 到 full-res 的升采样必须用边缘感知的 bilateral filter,否则会糊掉场景里的几何细节。一个常被忽视的细节:升采样时除了 depth/normal,还要把抖动向量(jitter offset)一并考虑——否则低频 AO 在 camera move 时会出现"呼吸感"。
// 典型 GTAO kernel (8 rays, 4 steps per ray)
float3 tangent, bitangent;
BuildOrthonormalBasis(normalize(input.normal), tangent, bitangent);
float ao = 0.0;
[unroll]
for (uint i = 0; i < 8; ++i) {
float3 rayDirTS = sampleDirections[i]; // precomputed on CPU
float3 rayDirWS = rayDirTS.x * tangent + rayDirTS.y * bitangent + rayDirTS.z * input.normal;
float t_max = 0.0;
[unroll]
for (uint j = 0; j < 4; ++j) {
float3 samplePos = input.positionWS + rayDirWS * (j + 1) * 0.05f;
float depthAtSample = SampleHiZ(screenUV(samplePos), j * 0.25f);
float dz = samplePos.z - depthAtSample;
t_max = max(t_max, dz);
}
ao += Falloff(t_max, 0.5f);
}
ao /= 8.0;
GTAO 与 HBAO/SSAO 的画面差异,在动态场景(角色在镜头前走动)上尤其明显——GTAO 因为积分方向是切平面方向,在掠射角下不会出现 HBAO 那种常见的"接触处变黑、稍远处变亮"的失真。
TAA 是这一组屏幕空间效果里历史最久但工程复杂度最高的一环。它做的事情简单:把当前帧的像素颜色与上一帧对应位置(用 motion vector 找到)的颜色做加权平均,权重由邻域的颜色距离决定。但工程上的难点集中在三件事上:抖动向量、history rejection、混合权重。
抖动向量(jitter offset):每帧在 NDC 空间里给投影矩阵加一个 sub-pixel offset,让每个像素在多帧之间轮询覆盖屏幕上的不同 sub-pixel 位置。理论上 8 帧就能覆盖一个像素格内的 8 个 sample,实际工程里常用 Halton 序列或 Bayer matrix 来生成 jitter。抖动向量必须和 motion vector、camera projection 同步——否则画面会出现"该清的没清、该留的没留"。
history rejection:把上一帧颜色采到本帧时,要判断这个采样是不是"有效"的——如果是有效的(邻域颜色接近、深度接近),就用它做加权平均;如果无效(颜色距离远、深度变化大、disocclusion 区域),就拒绝掉只用当前帧。这一步是 TAA 抗鬼影的关键,2026 年的工程实现普遍用 5x5 邻域做 min/max 裁剪(min/max neighborhood clipping)。
混合权重:历史颜色与当前颜色的混合权重,通常用一个置信度信号(比如历史颜色与当前颜色的余弦相似度)做加权。简单的 0.9 历史 + 0.1 当前是常见基线,但在高速运动的物体上需要降到 0.5 历史 + 0.5 当前以减少拖影。
AI 升采样器(TSR/DLSS/FSR/XeSS)在 2026 年已基本取代 TAA 成为主机/PC 端的主流。它们与传统 TAA 的核心区别在于:不是简单的加权平均,而是用一个神经网络(TSR 与 DLSS 是端到端训练,FidelityFX Super Resolution 2.x 是 handcrafted 算法但同样有可学习的 denoise kernel)预测"理想高分辨率"输出。这意味着它们在以下三件事上比 TAA 更强:
但 AI 升采样器的工程成本也更高——它们需要训练好的模型权重(每代硬件一版,比如 DLSS 在 RTX 40 系列上有一个版本,RTX 30 系列上有一个版本),需要 motion vector 的低精度变体作为输入,需要专门的处理单元(FP16/BF16 tensor cores)。2026 年的工程实践是:PC 端默认 DLSS 或 XeSS(硬件依赖),主机端用 TSR 或自研方案(模型权重可控),移动端继续 TAA 或 FSR 2.x(无 AI 单元)。
跨平台的升采样器选择矩阵如下:
| 平台 | 默认升采样 | 备选 | 关键约束 |
|---|---|---|---|
| PC RTX 40 系列 | DLSS 3.5+ | FSR 3.1 | tensor core FP16 |
| PC RTX 30 系列 | DLSS 3.x | FSR 3.1 | tensor core 较弱 |
| PC 非 NVIDIA | FSR 3.1 | XeSS | 无 tensor core 加速 |
| PS5 | PSSR(自研) | TSR | AMD RDNA 2 APU |
| Xbox Series X | PSSR(自研) | TSR | AMD RDNA 2 APU |
| Switch 2 | FSR 3.1 | TAA | 移动级 GPU |
| iOS / Android | FSR 2.x | TAA | 无 tensor core |
值得专门说的一个细节:AI 升采样器对 motion vector 的精度非常敏感。如果 motion vector 是 half precision 或 low-precision(移动端常见),升采样结果会出现明显的"残影边缘"。一个被反复验证的修复是:把 motion vector 在写入 G-Buffer 前先做一次 dFdx/dFdy 的 validation,剔除 NaN/Inf 后再量化;然后在升采样器的输入阶段做一次精度补偿。NVIDIA 在 RTX SDK 文档里专门写了一个章节讲这件事。
屏幕空间 GI(SSGI)是把 SSR 的思路推广到 GI 上——不仅反射直接光,还要反射其他表面的反弹光。算法上与 SSR 类似(屏幕空间射线),但积分更复杂(多 bounce)。2026 年的工程实践中,SSGI 的定位越来越尴尬:它的计算成本接近 SSR 的 2-3 倍,但收益(画面 GI 真实感提升)往往不如 DDGI 或 Light Probe 烘焙来得直观。
主流引擎的取舍是:静态场景用 Light Probe 烘焙 + 运行时 IBL 采样(开销极低、画面稳定);动态场景的局部 GI 用 DDGI(Dynamic Diffuse GI,基于 probe grid 的运行时 GI 算法)——DDGI 在 2026 年的工程实现里基本已经稳定(probe 数量 8x8x8 网格、每帧更新一次 probe、运行时 screen-space fallback),它的画面质量与 SSGI 接近但稳定性远好;只有当动态物体非常密集、动态光照非常关键(比如角色施法瞬间)时,才用 SSGI 做一次短时间的"补强"。
混合策略上,2026 年的标准做法是三段式:
这三层之间的过渡是渐变的:trigger 信号包括"动态光源数量 > 阈值""玩家施法瞬间""过场动画开始"等。这种设计的好处是 99% 的时间只跑前两层(总开销约 0.6 ms),触发补强层时画面质量提升明显但只持续几百毫秒。
移动端 GPU 普遍是 TBDR(Tile-Based Deferred Rendering)架构,代表是 ARM Mali、Apple GPU、PowerVR、Qualcomm Adreno。TBDR 的特点是:每个 tile 的 framebuffer 必须在 tile 完成前 resolve 到主存,这意味着 fragment shader 里的带宽压力比 IMR(Immediate Mode Rendering)架构小得多,但 latency 压力(尤其是 input attachment 的 read-after-write 依赖)非常大。
屏幕空间效果在 TBDR 上的适配有几个关键点:
第一,half-res 渲染要在 tile 内完成。SSAO/SSR 在 half-res 渲染时,如果 tile 大小是 16x16,half-res tile 就是 8x8——这种 tile 在 Mali 上会被合并到相邻 tile 一起调度。开发者要做的是:把 SSAO 的 input attachment(深度与法线)显式标记为 "tile-local",让 tile 内缓存可以跨 pass 复用;否则每个 SSAO pass 都会做一次 tile → 主存的 resolve,带宽直接爆炸。
第二,AI 升采样器在移动端基本不可用。Mali-G710 之后才有的稀疏 FP16 加速单元,但远不到桌面 tensor core 的吞吐。FSR 2.x 的 handcrafted 算法在移动端更现实——它不需要 tensor 单元,但代价是质量比 DLSS 低 1-2 档。
第三,bandwidth 是天花板。TBDR 的带宽由 tile 进出主存的次数决定。SSAO 在 1080p half-res 上,如果 input attachment 不是 tile-local,一次 tile resolve 就吃掉 1080x1920x4 byte = 7.9 MB——这是 mobile 上单帧带宽预算的 5%-10%。所以 mobile 上 SSAO 的"input attachment tile-local"是必走项,不是可选优化。
第四,frame pacing。TBDR 的 frame pacing 与 IMR 不同:render 一帧的时间由"最慢 tile"决定,而不是 frame 总时间。所以 mobile 后处理栈的设计要把"每个 pass 的 tile 工作量"作为关键指标——一个 tile 工作不均衡的 SSAO 会成为整个 frame 的 bottleneck。
工程上,2026 年 mobile 后处理栈的默认配置是:
| Pass | 移动端默认 | 移动端备选 | 关键约束 |
|---|---|---|---|
| SSAO | GTAO half-res | SSAO half-res | input attachment tile-local |
| SSR | FallbackOnly | HalfRes(仅当 GPU 余量 > 1ms) | 不启用 |
| TAA | TAA(无 jitter) | FSR 2.x | 避免 FP16 输入 |
| SSGI | 不启用 | — | — |
| Bilaterial upsampling | 5x5 边缘感知 | 3x3 双线性 | tile-local 输出 |
把 2026 年四个主流渲染栈的屏幕空间效果支持做一次横向对比:
UE5.6:后处理栈最完善。SSR 默认跑在 half-res + TAA-aware resolve,SSAO 默认 GTAO half-res,TAA 默认 TSR(配合 Lumen 时效果最好)。Substrate 材质系统对 SSR/SSAO 输入做了一些额外优化(法线更稳定、抖动更精细)。Nanite 与 Lumen 的联动让 SSR/Lumen 的混合成为默认——Lumen 提供 fallback,SSR 提供高质量反射。
Unity 6:URP 与 HDRP 分化明显。URP 的后处理栈更轻量,默认 TAA + 半分辨率 SSAO(无 SSR 默认开启)。HDRP 的后处理栈接近 UE5,但 SSR 集成度略低——HDRP 6 的 SSR 仍需要手动启用,且对虚拟几何(类似 Nanite 的方案)支持有限。
Godot 4.6:开源引擎里最完整的后处理栈。SSR、SSAO、TAA 都默认开启,但 AI 升采样器未集成(2026 年仍依赖 FSR 2.x)。Godot 4.6 的优势是渲染代码完全可读,适合学习但不适合直接拿生产级参考。
自研引擎:大多数 AAA 工作室的自研引擎都已经把屏幕空间效果作为后处理栈的标配,但 AI 升采样器要么集成第三方(FSR/XeSS)、要么自研——自研成本通常在 5-10 人年。
未来 2027-2028 年的方向,集中在三件事:
下面 12 条是 2026 年图形程序员处理屏幕空间效果时可以立即落地的工程实践。每条都已在前文分析过,按优先级排列:
建立 ScreenSpacePass 抽象层。把 SSR/SSAO/TAA/Composite 统一为一个 pass 框架,接受 PassParameters struct,内部用 enum 区分。至少能减少 50% 的样板代码。
Hi-Z pyramid 必须共享。SSR 与 SSAO 都要用 Hi-Z,不要各做一份。复用一份 Hi-Z 至少节省 1/3 的带宽。
half-res 是默认,full-res 是 escape hatch。所有屏幕空间效果默认 half-res,只在 cinematic camera + 玩家固定视点 + GPU 余量充足时切到 full-res。
TAA jitter 序列用 Halton (2,3)。Bayer matrix 在低分辨率(1080p 以下)会出现明显 pattern,Halton 序列更稳定。
AI 升采样器的 motion vector 必做精度 validation。NaN/Inf 必须在写入 G-Buffer 前剔除,half precision motion vector 在升采样器输入前必须做一次精度补偿。
history rejection 5x5 邻域是基线。3x3 在 dynamic 场景上不够,7x7 开销翻倍而收益边际递减。
TBDR 上的 SSAO input attachment 必标 tile-local。否则 tile resolve 会吃掉 5%-10% 的帧带宽。
避免双重时域滤波。SSR 与 TAA 不要各自做时域累积,统一交给 TAA history buffer。
fallback 永远要有。SSR 的 fallback 是 cubemap,SSAO 的 fallback 是常数 1.0,TAA 的 fallback 是 FXAA。任何一个 pass 失败时,fallback 必须能立刻接管。
profile 工具链:RenderDoc + PIX + 自家 GPU timing query 三件套缺一不可。GPU timing query 必须以 0.5 ms 精度输出每 pass 时间,这是定位屏幕空间效果瓶颈的基础。
跨平台升采样器策略:PC 用 DLSS/XeSS,主机用 PSSR/TSR,移动用 FSR 2.x/无 AI。永远不要在移动端硬上 AI 升采样器。
每帧的"屏幕空间效果总开销"作为可观测指标。把它做成 HUD 上的一个数字,超过 3 ms 时自动降级 fallback;超过 5 ms 时整个屏幕空间栈切到最低 quality。CPU 端只关心"这一帧花了多少 ms 在屏幕空间效果",不关心具体哪个 pass。
一份合格的 2026 年 AAA 项目,这 12 条都应落地。这不是建议,是入场券。
Conversation
0 条