技术博客

TSR 时序超分:历史帧重建的实现要点

第 41 / 100 篇 · 2025-07-14 · ue5 · TSR抗锯齿渲染源码分析

TSR(Temporal Super Resolution)是 UE5 默认的上采样方案:内部以低分辨率渲染(比如 1080p 渲染→4K 输出),靠历史帧累积把细节重建回来。它一半是抗锯齿(TAA 的职责),一半是上采样(TAAU 想做的事),但实现上比两者都激进。代码在 Engine/Source/Runtime/Renderer/Private/PostProcess/TemporalSuperResolution.cpp(UE5.3)。

问题设定:信息量从哪来

先看清楚输入输出的信息量。4K 输出有 830 万像素,1080p 输入只有 207 万——单帧信息不足以填满输出,必须有别的信息源。TSR 的信息源是时间:相邻帧之间相机和物体的亚像素抖动(jitter)让每个采样点落在输出像素网格的不同位置,把多帧的偏移采样拼起来,理论上能逼近甚至超过原生分辨率的有效信息。

用采样理论说:jitter 把每帧采样点散布在输出网格上,N 帧累积后有效采样密度提升 N 倍。重建滤波器要做的就是在无噪声的前提下把这些样本加权合成。TSR 的质量上限由历史帧数×每帧抖动覆盖度决定,而不是当前帧的分辨率——这也是它和单纯放大的本质区别:放大不增加信息,TSR 增加。

历史验证:TAA 的幽灵问题在超分下放大

时序重建的通用难题是历史像素是否还代表当前像素:物体动了、被挡了、光照变了,历史样本就是"幽灵"(ghosting)。普通 TAA 里幽灵是一块拖影;TSR 里由于输入输出分辨率差 4 倍,一个历史样本的错误贡献会被插值到 4 个输出像素,幽灵问题更严重。

TSR 的验证分两道:

  1. 深度/运动向量重投影。当前帧像素 $p$ 经运动向量回溯到上一帧位置 $p'$,取历史颜色。这是所有时序方案的标配;
  2. 颜色邻域钳制(neighborhood clamp)。在当前帧像素周围取 3×3 邻域,把历史颜色钳制到邻域颜色的 min-max 范围内(有实现用 AABB 加软边界而不是硬 min-max)。超出范围说明历史不可信,向当前帧颜色拉回。TemporalSuperResolution.cpp 里对应 History Validation 一组 pass。

普通 TAA 做完这两步基本够用,TSR 多做了一步:双向验证的可分离。超分场景下历史帧的角色是"高分辨率细节的来源",不是"去噪的 temporally累积"——所以 TSR 允许历史贡献超过传统 clamp 范围,但要用历史自身的置信度加权(历史帧在生成它的时候做了多少验证,置信度就存多少)。这让 TSR 在遮挡边缘比 TAA 更敢用历史,细节保留更好,代价是参数调错时闪爍(flickering)比 TAA 更难调。

与 TAAU 的差异:为什么 UE 要另起炉灶

TAAU(Temporal Anti-Aliasing Upsampling)在 UE4 后期就存在,思路一样(低分辨率累积+上采样),TSR 与之的差异值得专门说:

维度 TAAU TSR
上采样比例 一般 ≤2× 设计目标 2×,可更高
历史缓冲分辨率 输出分辨率 输出分辨率
重建滤波 固定核 自适应(按历史置信度)
抖动序列 低分辨率网格内 输出分辨率网格(更细的抖动)
去噪职责 依赖渲染端低噪输入 内置历史去噪,接受更噪的输入

关键在最后一行。TAAU 要求输入帧本身足够干净(SSAO、阴影等要先在低分辨率下收敛),否则上采样会把噪声也放大;TSR 把历史去噪纳入重建核,可以接受低分辨率下更激进的效果(更便宜的 SSAO、更少的阴影采样),把省下的预算让给主渲染。这是"上采样器吃掉整个渲染管线的去噪职责"的思路,也是 4K 输出配 1080p 渲染还能看的结构性原因。

TSR:抖动把采样散布到输出网格,多帧拼合重建 帧 N(输入 1/4 分辨率) 样本带亚像素抖动 输出 4K 网格上的累积 ● 帧 N ● 帧 N-1 ● 帧 N-2 重建 历史验证 重投影+钳制 置信度加权

实现要点:TSR 的 pass 链

TemporalSuperResolution 的 RDG 序列(简化):History Resample(从上一帧 TSR 输出按运动向量重采样)→ Reject/Clamp(邻域验证,生成每像素的历史置信度)→ Depth Repair(处理深度不连续处的历史失效)→ Filter(空间滤波压残余噪声)→ Update History(写回历史缓冲,供下帧用)。其中 Depth Repair 一步是 TAA 里没有的:TSR 的历史缓冲是输出分辨率的,历史像素和当前像素的深度对应关系在低分辨率输入下更容易错位,需要专门处理半遮挡边缘。

代价与调优

TSR 不是免费的午餐,账单在这几处:

  • 渲染分辨率降 1/4,GPU 时间大约省 30-40%(不是所有 pass 都按像素计费,vertex 密集和固定开销不受影响);
  • 显存增量:历史缓冲按输出分辨率存(颜色+置信度+深度),4K 下 ~100MB 级;
  • 闪烁调优是主要维护成本:r.TSR.* 参数族里,History.Validation 和 History.GrandReprojection 相关参数控制幽灵-细节的天平。闪得厉害先查运动向量是否正确(植被 WPO、皮肤骨骼的 motion vector 生成都容易漏),再动参数。

和 DLSS/FSR 的关系也值得说清:TSR 是引擎内的纯时序方案,不依赖专用硬件单元;DLSS 用 Tensor Core 跑网络超分,细节重建更敢猜(可能" hallucinate 细节")。UE5 项目里两者可共存(DLSS 插件替换 TSR 的输出端),选择依据是目标硬件分布和内容类型——植被密集内容 DLSS 占优,风格化/手绘内容 TSR 更不容易出鬼影。

源码入口

  • 主入口:TemporalSuperResolution.cpp 的 AddTemporalSuperResolutionPasses;
  • 历史验证逻辑:FilterTemporal、RejectShadingHistory 相关函数,重点看 clamp 用的邻域构造方式;
  • 参数文档:控制台 r.TSR. 前缀全部参数,r.TSR.History.ScreenPercentage 控制输入渲染比例(默认 50%,即 4K 输出配 1080p 渲染)。

TSR 的工程价值在于把"分辨率"从渲染预算的硬约束变成了可分配变量——同样的 GPU 时间,原生 4K 跑 30fps 还是 1080p 渲染 + TSR 跑 60fps,这个选择权在发行前都是开放的。理解它的信息论本质,才能在调优时不至于陷入参数盲调。