← ALL ARTICLES

时域采样与重投影

全库目录221
本文目录11

时域抗锯齿(TAA)把每像素多个采样点分摊到多帧。本文从投影抖动和指数累积进入历史坐标,再说明重采样、深度简化方案与超分;历史能否采用由 时域历史校验判断。空间抗锯齿与选型见 抗锯齿与时域重建。

时域抗锯齿

一条完整的 TAA 由两件事构成,后续所有技术都归属于其中之一:

text
样本累积          历史校验
  ├ 抖动采样        ├ 拒绝:判定失效则丢弃
  ├ 重投影          └ 矫正:拉回到合理范围
  └ 混合权重

每帧的数据流是:抖动投影矩阵渲染出新样本,用运动向量把上一帧输出重投影到当前位置,校验这份历史是否可信,再按权重与新样本混合,结果既是本帧输出也是下一帧的历史。

抖动

每帧给投影矩阵加一个亚像素偏移,使采样点在像素内均匀覆盖。

写入投影矩阵

先明确约定:抖动 jj 以像素为单位,屏幕宽高为 (W,H)(W,H),对应NDC偏移为 (2jx/W,2jy/H)(2j_x/W,2j_y/H)。屏幕Y向下而NDC向上时还需反转Y符号。要让透视除法后的坐标增加该偏移,应在齐次裁剪坐标上加偏移乘w:

hlsl
// jitterNdc已按屏幕原点和投影约定转换;positionCS为未抖动位置
positionCS.xy += jitterNdc * positionCS.w;

将这一操作合并进投影矩阵时,矩阵元素的索引和符号由行列向量及视空间前向决定。验证方式是冻结场景,给出正0.5像素的抖动,确认所有深度处的图像都沿约定方向平移0.5像素。

序列的选择

物体可能在任意时刻出现或被遮挡揭露,因此一个像素的累积可以从序列的任意位置开始。理想序列需要满足:任意长度的任意子序列都大致均匀分布。

低差异序列能改善连续样本的覆盖,Halton(2,3) 是常用选择,但不保证任意长度、任意起点的子序列都均匀。短序列仍需检查分布与运动相关性。序列长度按目标质量取,常见 8 到 16 个样本。

运动会打乱精心设计的采样分布:短周期序列配合特定运动速度时,多帧的采样点可能在世界空间聚成一簇,导致结果偏移。随机化抖动模式可以打破这种规律性。

纹理采样偏置

纹理本身已经过 Mipmap 过滤,再经 TAA 平均会过度模糊。可试验负Mipmap偏置以保留更高频细节。若将独立有效样本理想化为二维均匀分布,线性采样间距约缩小到原来的 1/Neff1/\sqrt{N_{eff}},可得到偏置的启发值:

bias=−12log⁡2Neffbias=-\tfrac{1}{2}\log_2 N_{eff}

有效采样数为 4 时偏置为 −1.0-1.0。实践中常取比理论值保守一些的数值,因为过度偏置会损害时域稳定性与纹理缓存效率。这一点在时域超分下尤其重要。

历史累积与重投影

样本累积

存下每个像素的全部历史样本不现实。主流做法是把累积结果平均成单一颜色,它既是当前帧输出,也是下一帧的历史输入:

fn(p)=α⋅sn(p)+(1−α)⋅fn−1(π(p))f_n(p)=\alpha\cdot s_n(p)+(1-\alpha)\cdot f_{n-1}(\pi(p))

sns_n 是本帧新样本,π(⋅)\pi(\cdot) 是重投影算子,α\alpha 是混合系数。

固定系数等价于指数平滑

在历史对应固定、系数不变且未发生校验重置时,递推式在稳态可展开为下式;有限帧初始化残留另计:

fn(p)=α(sn+(1−α)sn−1+(1−α)2sn−2+⋯ )f_n(p)=\alpha\left(s_n+(1-\alpha)s_{n-1}+(1-\alpha)^2s_{n-2}+\cdots\right)

越旧的样本权重越低。这在多数情况下是合意的——旧样本更可能已经失效。

但从方差削减的角度看,这是次优的:对独立、同分布且等方差的静态样本,等权平均使方差最小。时域样本有相关性、运动和可信度差异时,最佳权重需要考虑这些条件。固定 α\alpha 在样本数很少和很多两端都会损失质量。

等效采样数

固定 α\alpha 累积 NN 帧,相当于多少次等权重超采样?这个换算直接决定了参数选择:

累积帧数等效采样数(α=0.1\alpha=0.1)
5约 2.2
10约 5.1
15约 9.8
稳态约 19

表中用首帧样本初始化历史;第N帧最旧样本的权重为 (1−α)N−1(1-\alpha)^{N-1},其余权重为 α(1−α)k\alpha(1-\alpha)^k。在独立等方差假设下,Neff=1/∑iwi2N_{eff}=1/\sum_iw_i^2,稳态为 (2−α)/α=19(2-\alpha)/\alpha=19。它衡量方差,不能直接等同于19倍MSAA的覆盖或运动质量。同时也说明收敛需要时间,相机切换后的前几帧质量明显偏低。

等权重方案

在静态、独立等方差样本的前提下,可以在历史缓冲的 Alpha 通道存逐像素累积样本数 Nt(p)N_t(p):像素刷新时置1,此后每帧递增,取 α=1/Nt(p)\alpha=1/N_t(p)。未发生历史变形或拒绝时,这让历史样本保持相同权重。

代价是多占一个存储通道。它适合需要在遮挡揭露后快速收敛的场景,尤其目标帧率较低时。

历史被拒绝时将Nt(p)N_t(p)重置为1;颜色裁剪改变了历史值,可按配置重置或衰减有效计数。经过裁剪后的计数是刷新权重的工程估计,不再代表完整保留的等权独立样本数。

α\alpha 的下界

α\alpha 过小时结果容易受重采样误差与时域滞后影响。通常给它设一个下界,保证历史有最低限度的刷新率。下界可以随运动速度提高——运动越快,重采样误差累积越快,越需要更多新数据。

在管线中的位置

直接平均HDR颜色时需要处理高亮离群值:高动态范围下,少数极亮样本会主导平均值,边缘梯度被破坏。

解决办法是在 TAA 前后各做一次色调映射与其逆变换:

text
HDR 渲染 → 可逆 Tonemap → TAA → 逆 Tonemap → HDR 后处理 → 最终 Tonemap

中间这次使用简单可逆的算子即可,例如非负标量 Reinhard 形式 x/(1+x)x/(1+x),逆变换为 y/(1−y)y/(1-y),它只作为最终色调映射的代理。

亮度自适应权重

另一条路径是在混合时按亮度加权,避免色调映射导致的去饱和:

w(c)=11+L(c)w(c)=\frac{1}{1+L(c)}

L(c)L(c)取工作颜色域中的非负亮度,输入颜色与亮度均需为有限数。令当前颜色为cc、已通过校验的历史为hh,分别计算a=αw(c)a=\alpha w(c)与b=(1−α)w(h)b=(1-\alpha)w(h),结果为(ac+bh)/(a+b)(ac+bh)/(a+b);当前样本权重α\alpha在[0,1]内。归一化保证常量颜色保持原亮度,仅把颜色乘权后相加会使结果变暗。所有颜色通道共享同一亮度权重,它可降低极亮样本对混合的支配程度;裁剪或拒绝历史仍由历史校验处理。

重投影

运动向量

运动向量记录表面点在屏幕空间的投影位移。先用上一帧与当前帧的变换得到同一表面点的裁剪空间位置,分别除以各自的w,再转换为相同原点约定的屏幕UV。本文定义 v=uvcurrent−uvpreviousv=uv_{current}-uv_{previous},消费时用 uvprevious=uvcurrent−vuv_{previous}=uv_{current}-v;直接相减两个裁剪空间位置会把透视尺度混入位移。

例如两帧裁剪坐标x均为2,当前w为4、上一帧w为2,对应NDC横坐标为0.5和1,因此UV横向速度为-0.25,而不是0。这里要求两帧位置都能有效投影;上一帧位于相机后方或历史UV越界时应拒绝历史。

精度要求较高,常用两通道 16 位浮点格式存储。一种清晰的约定是运动向量排除投影抖动,由重投影阶段单独补偿前后帧抖动差。其他实现也能把抖动编码进向量,但生产与消费必须使用同一约定。若当前历史查找起点uv包含本帧抖动,运动v排除抖动,且两份历史缓冲使用各帧抖动坐标,则上一帧查找为uv-v+jitterPreviousUV-jitterCurrentUV;两个抖动都先换成同原点约定的UV单位。若生产端已经包含抖动差,消费端便按其契约直接减v。

骨骼动画需要上一帧的骨骼姿态;UV动画若能建立明确的表面流动对应关系,可另外输出运动;一般贴图动画不能仅把UV偏移直接当成屏幕位移;平面反射需要推导反射矩阵对应的运动关系。

为节省带宽,部分引擎只为动态物体显式写运动向量,并用模板标记;未标记像素在 TAA 阶段用深度实时反算:

pn−1=Mn−1Mn−1pnp_{n-1}=M_{n-1}M_n^{-1}p_n

pnp_n 是当前帧像素的裁剪空间齐次坐标,MM 是各帧的视图投影矩阵,结果经透视除法得到。

复杂贴图动画、粒子、烟雾、水流和多层透明未必具有可由单个向量表达的对应关系。例如一个像素混合了两个反向运动的透明表面,任取一个向量都会错配另一层。这类内容可采用响应遮罩、分层历史或更高刷新权重,误差是否可接受应按具体运动和覆盖验证。

采样邻域最近深度

直接用当前像素的运动向量,在遮挡边界处可能取到错误的历史位置。改用邻域中离相机最近的那个点的运动向量,可以让前景的运动向量“覆盖”边界像素,减弱遮挡变化造成的错误。

这是HLSL机制片段:SampleDepth按点采样读取同一帧深度,越界坐标钳制到有效纹素;CompareDepth(a,b)在a表示的表面更近时返回1,否则返回0,相等时保留原结果。普通Z与反向Z分别实现这份距离语义;这里只选邻域位置,随后在返回位置取得运动向量。

hlsl
float2 GetClosestFragment(float2 uv)
{
    float2 k = _CameraDepthTexture_TexelSize.xy;
    float3 result = float3(0.0, 0.0, SampleDepth(uv));

    // 比较四个对角邻居,保留离相机最近的偏移
    result = lerp(result, float3(-1, -1, SampleDepth(uv - k)),
                  CompareDepth(SampleDepth(uv - k), result.z));
    result = lerp(result, float3( 1, -1, SampleDepth(uv + float2(k.x, -k.y))),
                  CompareDepth(SampleDepth(uv + float2(k.x, -k.y)), result.z));
    result = lerp(result, float3(-1,  1, SampleDepth(uv + float2(-k.x, k.y))),
                  CompareDepth(SampleDepth(uv + float2(-k.x, k.y)), result.z));
    result = lerp(result, float3( 1,  1, SampleDepth(uv + k)),
                  CompareDepth(SampleDepth(uv + k), result.z));
    return uv + result.xy * k;
}

该片段用选择运算表达更新;编译器如何生成分支或谓词需看输出,不能由lerp写法保证性能。深度比较方向取决于是否启用反向 Z,必须按约定推导而非照抄符号。取五点或九点邻域是质量与成本的权衡。

另一种做法是对前景物体做小范围膨胀(常用四抽样窗口),让边界像素随前景一起重投影。边缘像素可能包含多个表面,简单平均不同表面的向量并不能建立正确对应;缺少边缘处理时,运动物体的平滑边缘可能重新出现锯齿。

重采样滤波

重投影位置通常落在像素之间,需要重采样。双线性采样最廉价,但保留高频的能力差,而且误差每帧累积——历史反复经过重采样,很快就糊得明显。

改进方向是使用更高阶的重采样核:

  • Catmull-Rom 三次插值,可利用双线性采样合并相邻正权项;完整二维核和缩减为五次采样的近似核应分别比较,后者需要说明删减的贡献及归一化方式;
  • 前后误差补偿(BFECC) 通过反向重采样估计误差并修正;
  • 带预滤波的广义重采样可在其信号和边界假设下减少反复重采样误差,代价包括额外滤波与存储。

反复重采样会放大滤波器差异。比较时固定图像、位移和边界规则,分别测量模糊与振铃,不能把某个重采样次数下的结果当作全部内容的保证。每帧混入新样本可以部分缓解累积模糊,但不能消除。

仅用重投影的简化方案

省去动态物体运动向量,只靠深度和相机矩阵反算重投影。物体相对场景运动时,这条路径缺少对应位移;它减少了动态向量生成与读取,实际收益按目标平台验证。

静态场景中,深度重投影可复用屏幕空间环境光遮蔽等效果的历史。反射命中点、体积积分区间与当前可见表面可能没有相同运动,需另外设计重投影及拒绝条件;相机矩阵本身不能解释这些信号的移动。

随机化效果可以借用历史累积摊薄采样,但必须分别定义被积信号与历史对应。例如AO通常跟随当前表面,反射却还依赖反射命中物与视角。只给效果随机样本而复用表面运动,不足以保证积分有效;无法建立对应关系的区域应提高刷新或拒绝历史。

时域超采样

把每像素采样率降到一个以下,累积到更高分辨率的缓冲,TAA 就成了超分方案。

由于输入样本与输出像素不再一一对应,需要先按重建核把输入样本升采到输出分辨率,再按质量置信度决定混合强度:

fn(p)=αβ(p)sˉn(p)+(1−αβ(p))fn−1(π(p))f_n(p)=\alpha\beta(p)\bar{s}_n(p)+(1-\alpha\beta(p))f_{n-1}(\pi(p))

β(p)∈[0,1]\beta(p)\in[0,1] 表达该输出像素本帧获得的样本质量。公式以该位置存在有效历史为前提;相机切换、历史拒绝或首次出现的像素先用当前输入的空间重建结果初始化,再建立历史与置信度。β=0\beta=0时保持历史只适用于这份历史已经有效的情形,不能读取未初始化内容。靠近实际采样点的像素置信度高,仅由远处样本插值而来的置信度低。合理的抖动模式下,每个输出像素每隔几帧就能获得一次高质量样本。

以宽高各放大两倍、盒式核为例:输入与输出是 1:4 映射,每帧 2×2 输出块中只有一个像素的 β\beta 为 1 并被更新,其余三个保持历史不变。这个框架支持任意超分倍率。

超分下输入更稀疏,历史校验比常规 TAA 更困难,各类伪影也更明显。

验证方法

单独显示运动向量,检查相机、骨骼和顶点动画;固定场景施加正0.5像素抖动,核对方向与不同深度的一致平移。关闭抖动后比较空间滤波和历史累积;相机瞬切后检查初始化与收敛。CPU权重展开得到5、10、15帧约2.17、5.13、9.78个等效样本,稳态约19,前提是独立等方差样本。检查见 reference.py,运行 python 知识库/examples/mechanisms/reference.py。UV方向与w不同的运动检查见 test_raster.py,运行 python -B 知识库/examples/revision/test_raster.py。HLSL为机制片段,完整TAA工程、引擎画面、GPU捕获与性能另验。

2026-10-24,正文片段验证器从当前文章抽取投影抖动与邻域最近深度片段,补齐显式常量、入口与采样辅助函数后通过DXC离线编译。验证器合计覆盖五个入口;深度宿主采用普通Z与点采样钳制约定,完整管线、GPU执行和画质仍需目标工程验证。

相关主题

参考资料

  • MJP,Tex2DCatmullRom:2026-10-22核读四项权重、合并中间采样的9次实现,以及作者对省略四角形成5次近似的说明;本文不引用来源设备的耗时作为本机性能。
  • Marco Salvi,An Excursion in Temporal Supersampling:2026-10-22核读第12—24页及38—45页的EMA、颜色盒、方差裁剪与运动复用;来源案例不作为本文运行结果。
  • 等效样本数和颜色盒裁剪的具体输入输出由本库CPU实验验证;跨帧画质与性能保留目标工程验收。