实时光追降噪新方案:ATAA如何根治动态场景闪烁与拖影
半年前写光追降噪第一篇时,评论区有人问:降噪做到最后,画面还会闪吗?我当时的回答是:会,而且越是在运动场景里闪得越明显。今天这篇算是这个系列的第二篇,聊一个我们后来在项目里实际验证过的新解决思路——ATAA。我按工程里最常见的全称展开:Adaptive Temporal Anti-Aliasing,也就是自适应时域反走样。它和我上一篇写的空间滤波式降噪有一个根本区别:以前我们是把每一帧稀疏光线的结果尽量擦干净,ATAA 则直接去管理“样本该怎么花、历史该信多少”,把采样分配和降噪串进了同一个闭环。
这篇适合正在给实时光追管线做降噪的渲染工程师,也适合对图形学有兴趣的技术美术。如果你刚接触光追降噪,前两章可以帮你建立完整的判断框架;如果你已经在调 SVGF、双边滤波这类方案,可以直接跳到第三章看 ATAA 的实现细节和第四章的排坑经验。写之前先给结论:ATAA 不是某个单一滤波核,而是一套以像素状态机为中心的时域累积框架,它解决的是老方案在动态场景里的两个痛点——拖影和闪烁。
1. 老降噪方案为什么会在动态画面里翻车
1.1 噪声不止是“噪”,更致命的是“时序不稳”
要理解 ATAA 的价值,得先说清楚被噪声折磨到底是什么感觉。实时光追为了性能,每像素往往只给 1 到 2 条光线,蒙特卡洛积分的方差反比于样本数,也就是说想靠加光线把噪声减半,得把样本数翻四倍,这条路在实时管线里基本走不通。于是必须靠降噪。可降噪这事最麻烦的不是当帧有噪声——毕竟一帧画面里有点颗粒感,静态截图还能忍——而是帧与帧之间同一像素的亮度反复跳动。
我打个比方:静态噪声像你在灯光昏暗的房间里看一张照片,颗粒感虽然明显,但眼睛可以“脑补”出细节。时序闪烁则像是同一张照片被人拿着手电筒来回扫,某个区域忽亮忽暗,注意力全被闪点吸走了。在照片级渲染里,只要某个高光边缘的亮度跳动幅度超过阈值,观众第一眼就会判定“这个画面很脏”。而且闪烁在视频压缩时会额外浪费码率,画面一动起来码率分配全在闪点上,边缘细节反而被压糊。所以光追降噪不能只盯着“让单帧方差变小”,更重要的是让时域信号稳定下来。
这也就是为什么几乎所有实时降噪器都要引入时域滤波的原因。但你一旦开始用历史帧,新的麻烦就来了:历史帧对应的内容是上一帧的,运动之后这个内容还在哪里、还该不该信,全靠重投影和置信度判断。判断一旦出错,轻则模糊,重则拖影、鬼影,甚至把上一帧的物体残影留在画面里。
1.2 空间滤波、时域递归和重投影,老思路卡在哪
以前主流做法可以拆成三个模块:空间滤波、时域递归、重投影误差检测。每个模块单独看都很合理,连在一起就有互相打架的地方。
空间滤波负责把当帧噪声抹平,常见的有双边滤波、A-trous 小波滤波。这类滤波核的问题很典型:核半径拉大,画面是干净了,高频细节也跟着没了;半径缩小,细节保住了,但噪声滤不干净。为了兼顾,只能在边缘检测上下功夫,比如用深度差、法线差、亮度差来控制权重。可真实场景里边缘往往同时夹杂几何轮廓和材质变化,检测参数稍微敏感一点,边缘就会过冲,出现一圈黑色描边;稍微迟钝一点,又会在高光反射边缘漏光。SVGF 那类时空方差引导的方法缓解了这个问题,但没有真正跳出“给定稀疏光线、输出一张干净图”的框架。
时域递归则是用上一帧结果和当前帧加权平均。静止画面下效果特别好,因为每一帧都在往同一个统计池里积累样本,等效 spp 可以做到几十甚至上百。但物体一旦运动,问题立刻暴露:历史帧对应的是旧位置,你必须通过运动矢量重投影到当前位置再混合。运动矢量只要有一点点偏差,结果就会在运动边界上出现残影。更麻烦的是遮挡切换,比如角色从柱子后面走出来,柱子后面那几帧的“历史”其实属于墙,你要是继续复用,就会看到墙的颜色慢慢留在角色身上。
重投影误差检测可以识别一部分这类问题,通常做法是拿当前帧深度与历史深度对比,差异太大就降低历史权重。但实际场景里有大量模棱两可的情况:高速旋转、半透明物体、细小物体穿过屏幕,深度对比往往来不及反应。等到判断结果出来,错误信息已经混进历史缓冲,而且因为时域递归有“记忆”,这个错误还会在后续几帧里慢慢衰减,观感上就是一条脏尾巴。
把这些放在一起看,老方案的共同问题就清晰了:它们把降噪当作后处理,光线怎么采样已经定了,滤波器只能被动接受一个不完美的输入。而 ATAA 的思路是,既然噪声分布不均匀,那干脆在采样阶段就针对高方差区域多给样本,针对稳定区域少给样本;同时把时域历史当成一种“带先验信息的样本池”,用置信度决定从里面捞多少东西出来。这是从源头上改变信息获取方式,而不是在画面已经坏了之后再补救。
2. ATAA 的设计内核:把降噪拆成“自适应采样”和“历史信任管理”
2.1 名字背后的三层含义
ATAA 的工程展开是 Adaptive Temporal Anti-Aliasing,三个词都值得拆开看。
Adaptive 指的是整套机制不是用一个全局参数来降噪,而是逐像素动态调整采样预算和历史复用强度。这也是它区别于传统 TAA 的最大一点:传统 TAA 的混合系数通常是固定值,顶多在边缘做一次二值选择;ATAA 则是一个连续决策系统,每个像素在每一帧都独立判断自己处于什么状态。
Temporal 指的是它对历史帧的态度。传统降噪把历史当作一个需要小心使用的缓存,ATAA 则把历史当作一个“免费样本池”。只要置信度高,几个像素点的时间积累可以等效于上百条光线效果。这个视角转换很重要,因为它意味着降噪质量不再只取决于当帧光线数,还取决于时域积累的时长和信心。很多实时渲染器允许每像素 1 到 4 spp,但靠时域积累可以稳定输出接近 64 spp 的效果,这就是时间换质量的典型。
Anti-Aliasing 这个名字可能让人困惑:降噪怎么和反走样扯上关系了?其实在实时渲染里,走样和噪声本来就没法完全分开。几何边缘的锯齿、高光反射的闪耀、时域采样不足引起的闪烁,本质上都是信号重建失败。ATAA 把采样决策和时域混合统一处理,所以“降噪”和“抗锯齿”在它内部是同一件事的两面。你可以理解成:它不只把现成的像素颜色弄干净,还会决定这些像素下一次该以什么样的采样密度来生成,以此同时压制边缘锯齿和时序噪声。
2.2 三类置信度决定“该采多少”和“该信多少”
要让自适应系统工作,必须给每个像素算出一个或几个置信度指标。ATAA 在实践中通常会计算三类置信度:几何置信度、辐射置信度和感知置信度。
几何置信度判断的是“上一帧的内容能不能重投影到当前位置”。输入信号是深度差、法线差、材质 ID 差、速度场连续性。当深度和法线在时间上变化很小,说明这个位置大概率来自同一表面,可以安全复用历史信息。反之,出现遮挡切换、物体边界、材质变化时,几何置信度会迅速降低。实际实现里,我通常把深度差容忍度设为当前像素深度的千分之几,同时用法线夹角余弦来判断表面朝向变化。
辐射置信度判断的是“当前观测信号稳定到值不值得建立长期平均”。输入是当前帧多条光线采样之间的方差、历史缓冲区里记录的方差估计。如果方差很大,说明这里本来就需要更多样本,盲目把历史权重拉高只会让“脏信息”被长期保留。反过来,方差小的地方哪怕历史只有几帧,结果也非常可信。这部分逻辑和 SVGF 里的方差估计很像,但 ATAA 会把方差结果直接作为下一步分配采样预算的依据,而不是只用来引导空间滤波强度。
感知置信度则是比较“软”的一类因子,用来照顾人眼的主观敏感度。比如暗部区域,特别是接近纯黑的场景,噪声的绝对幅度很低但相对变化可能很大,此时需要相对更多的时域积累;亮部高光区虽然方差大,但高光往往被眼睛视为“亮点”,容忍度反而高。再加上视点运动速度:摄像机快速转动时,人眼对细节不敏感,这时可以降低降噪强度,把算力省给真正需要的帧。这部分实现起来比较灵活,不同引擎的侧重点也不一样,但它提供了一个调节入口,让美术同学可以按镜头观感做参数微调。
这三类置信度最终会合成一个综合指标,驱动后面的像素状态机。干净的平面反射区几何置信度高、辐射方差小,系统会进入“加速积累”状态;运动物体边界则几何置信度低,系统知道历史不可靠,会主动减少历史依赖,转而多采当帧样本。
2.3 像素状态机:加速积累、动态更新、重新采样
前面说的置信度不是直接用做一个连续数值就完事,工程上我更建议把像素归类到三种离散状态里。为什么要离散?因为连续混合系数在大量像素上会产生不稳定的抖动,帧与帧之间的状态频繁变化会让画面出现另一种形式的噪声。离散状态加上滞后判断,反而能让累积行为更稳定。
状态 A 是加速积累,针对长时间稳定的区域。比如固定机位下的一堵墙面、静态桌面的漫反射部分。这些像素方差很小,历史信息可以被信任,系统把混合系数 alpha 设得很低,让当前帧颜色慢慢融入长期积累结果。等效样本数会随着帧数不断增长,实现低噪声效果。
状态 B 是动态更新,针对运动物体或光学变化区域。这些像素每帧都在变化,历史内容不能完全沿用,但又没有完全失效。此时 alpha 会抬到一个中等水平,比如 0.2 到 0.35,让系统快速响应变化,同时保留少量时间平滑来压制逐帧抖动。这个状态做得好不好,直接决定动态画面会有多少残影。
状态 C 是重新采样,针对遮挡切换、突然出现的反射、粒子效果刚生成的区域。历史信息基本不可用,系统必须回到当帧高权重甚至完全忽略历史的状态,同时分配更多光线给这些像素,让它们尽快建立起可靠的新统计。比如角色从遮挡物后走出时,新露出的皮肤表面需要立刻建立新历史,而不是沿用之前墙壁的颜色。
状态切换不是每一帧重新算一次就完事,至少要有时间滞后。我调试时发现,如果状态判断完全基于当帧瞬时结果,物体边缘的像素会在 A 和 B 之间来回抖动两三个帧周期,画面边缘就像水波纹一样闪。解决办法是给每个状态一个“冷却帧数”,例如从 A 切到 C 必须连续两帧判定为 C 才会真正切换,避免单帧误判引起抖动。
3. ATAA 关键实现细节与实操流程
3.1 先把 G-Buffer 和统计缓冲准备好
落地实现 ATAA,第一步不是写滤波核,而是把需要的输入缓冲整理好。主要分四类:几何数据、运动数据、辐射数据和统计数据。
几何数据包括深度、法线、粗糙度、材质 ID,直接来自渲染管线的 G-Buffer。辐射数据是当前帧光追采样得到的线性 HDR 颜色,这里强调一定是线性空间,不能在 sRGB 或伽马空间里做时域积累,否则混合结果会偏色。运动数据必须是帧间真实 3D 运动矢量,而不是 UI 层那种深度方向的简化版本。统计数据则是 ATAA 自己维护的历史信息:历史颜色缓冲、历史方差缓冲、历史置信度缓冲、以及每个像素当前所处的状态和累积帧数。
以 1080p 为例算一下内存成本。历史颜色用 RGBA16F,约 16.6MB;历史方差用 R16F,约 4.1MB;置信度用 RGBA8 打包,约 8.3MB;状态图和累计帧计数可以压缩到一个 R8 缓冲里,约 2.1MB。再加上双缓冲和临时缓冲,整体峰值占用大约 50MB。这笔开销放到现在的主流显卡上可以接受,但如果你在主机平台开发,内存预算比较紧,需要把置信度从 RGBA8 压缩到两个 R8,或者把状态和累积帧数编码进一个 32 位缓冲。
更新统计缓冲时有一个容易忽略的点:不是所有像素每一帧都有实际采样值。光栅化可以覆盖所有像素,但光追采样往往按分配器给出的 SPP 在部分像素上新增光线。对于没有新采样输入的像素,你不能把它们的历史信息清掉,而应该沿用上一帧的统计结果,只对时间衰减做一次处理。这样能避免采样点稀疏导致的历史空洞。
3.2 分配器:下一帧给每个像素多少条光线
ATAA 真正花心思的地方在采样分配。实时光追总预算固定,比如每帧允许 100 万条半透明射线,超过就会掉帧。分配器的任务就是把这 100 万条射线花在最值得花的地方,而不是均匀铺满全屏。
分配的基本策略是:先给每个像素一个基础 SPP,通常是 1,保证全屏都有信号;然后用上一小节的三类置信度计算额外预算。几何置信度低、辐射方差大的像素获得更多采样权重。具体做法是把屏幕分成 8x8 或 16x16 的块,先统计块内需要额外采样的像素数量,再按比例分配预算到块,最后在块内用性能优先的分配算法决定每个像素的 SPP。块级分配的原因是 GPU 上射线生成和遍历的开销通常按 warp 组织,如果相邻像素的 SPP 差异过于离散,线程发散会很严重,导致实际加速效果远低于理论模型。
不过这里有一个约束:总预算必须守恒。你不能只按“该采多少”来决定最终 SPP,否则高方差区域会把所有预算吃光,平坦区域反而没有样本。我通常用一个动态优先级表,先圈出高优先级像素,比如刚进入状态 C 的新表面、高光边缘、半透明区域,保证它们至少达到 4 到 8 spp;剩下的预算再按方差比例分给其他像素。最终每个像素的 SPP 会落到 0 到 16 之间,均值保持在每像素 1 到 2。
这一步还有个工程技巧:把分配结果作为一张显式纹理输出,而不是藏在 kernel 内部。这样你可以直接可视化每个像素实际拿到的 SPP,排查性能热点时非常直观。我之前遇到过一个场景:地面反射区颜色一直不稳定,后来看可视化才发现分配器给它的 SPP 只有 0,因为几何置信度误判成“遮挡”,导致它一直没有新鲜采样。这种问题要是不可视化,排查起来像大海捞针。
3.3 空间滤波只用来做“来不及积累”的补充
把采样分配做好之后,大部分像素已经在时域上足够干净了。但仍有两类像素需要空间滤波干预:一类是刚刚进入重新采样状态、历史还没来得及建立的像素;另一类是运动太快、时域积累窗口被切断的像素。ATAA 里的空间滤波强度应该设置得比传统降噪器保守,因为它的角色是补漏,不是主力。
实现上我会选 A-trous 分解,分四到五个尺度,从 3x3 开始逐级扩大滤波核半径。权重计算综合考虑深度差异、法线差异和颜色差异,其中颜色差异要放在对数亮度空间里做,避免高光边缘出现暗边。具体参数可以是:深度差异阈值 0.01 到 0.05 倍当前深度,法线夹角大于 15 度视为边缘,颜色差异阈值约为局部 StdDev 的两倍。
有一点要特别留意:空间滤波不要跨过状态边界。状态 A 和状态 B 的像素如果被同一个滤波核混合,会同时损害两者的统计信息。我干脆把状态图作为 A-trous 的引导通道,滤波核只能采样与自己状态相同的像素,或者在状态 C 的区域完全关闭空间滤波。否则你会看到动态物体边缘出现一圈“亮晕”,那就是状态 C 的像素被状态 A 的长时间积累结果污染了。
空间滤波的输出需要在下一帧回写到历史颜色缓冲,但历史方差缓冲不要直接覆盖,而是用一个比较低的速率更新。原因是方差本身是慢变量,突然把一个像素的方差从大改小,会触发后续帧的分配器大幅降低该像素的 SPP,进而造成新的不稳定。我实测下来,方差的更新速率保持在 0.1 到 0.2 之间比较稳。
3.4 时域分享:让历史帧变成真正的“免费样本”
时域分享是 ATAA 的核心收入来源,也是最容易出错的部分。基本操作是用当前帧的运动矢量,把历史缓冲重投影到当前帧位置,然后和当前帧颜色做加权平均。但细节决定了最终观感。
首先,混合系数 alpha 不能只有一个全局值。按状态机思路:状态 A 的 alpha 设为 1.0 除以累积帧数,也就是越积越慢,比如第 10 帧时 alpha 是 0.1,第 60 帧时变成 0.016;状态 B 的 alpha 固定在一个较高值,通常 0.2 到 0.35;状态 C 的 alpha 直接等于 1.0,即完全采用当前帧。这样亮度、颜色都能快速跟上场景变化,又不会引入历史错误。
其次,颜色钳制要谨慎使用。传统 TAA 会用 AABB 或颜色钳制把历史颜色的极端值限制在当前邻域范围内,防止历史残影。但在 HDR 光追里,高光反射常常就是那个“极端值”,盲目钳制会把高光削平。我现在的做法是只在结果超出历史均值加减若干倍标准差时才做钳制,倍率通常取 2 到 3。低于这个阈值,就相信历史,不做干预。
再次,时域分享里最容易被忽视的是“历史置信度衰减”。即使一个像素长期处于状态 A,它的历史置信度也不能无限积累。因为场景光照可能缓慢变化,比如天空颜色从黄昏过渡到夜晚,或者一次爆炸闪光改变了整个场景亮度。我每个像素维护一个置信度累积值,每帧乘以 0.95 左右的衰减因子,让系统对全局变化保持响应能力。衰减太快会损失降噪效果,衰减太慢则会在大范围亮度变化时出现很长的“亮度滞后”,画面像拖着一个尾巴。
关于半透明物体,我要多提醒一句:运动矢量在透明物体边缘经常失效,因为折射会让视线路径发生变化,重投影位置完全错误。这类像素建议直接判为状态 C,不要试图做时域积累,或者单独用另一套基于折射偏移的投影通道。剪不断理还乱,硬复用历史只会让透明物的边缘闪烁。
4. 工程集成、效果验证与排坑实录
4.1 管线位置和格式选择
ATAA 应该放在光追采样完成之后、后续后处理之前。最推荐的顺序是:G-Buffer 和光线生成 → 采样分配器 → 光线追踪采样 → ATAA 降噪 → 后续 TAA 或超分 → 色调映射。如果你在接某个商用引擎的时域超分方案,ATAA 输出的应该是线性 HDR 颜色,因为超分和后续的后期效果会再做一次时域处理。如果你把 ATAA 的降噪结果提前转到 sRGB 空间,之后再上超分,颜色会被二次变换,整个反射颜色都会偏掉。
还有一个常被忽略的衔接细节:运动矢量必须是 ATAA 和超分方案共用同一套。如果你让 ATAA 用自己的运动矢量,超分又用另一套运动矢量,两套数据之间稍有偏差,观众就会看到画面边缘出现“重影错位”效果。稳妥做法是在渲染管线的早段生成一套高质量运动矢量,垂直流动给所有需要时域处理的模块。
4.2 性能、显存与带宽估算
我拿一个中等偏上显卡、1080p 分辨率、每像素平均 1.5 spp 的测试场景做过一次性能记录,数据大致如下。需要说明的是这只是单场景参考值,不同引擎和硬件会有出入,但量级可以给你一个预期。
| 阶段 | 主要工作 | 耗时估算 |
|---|---|---|
| 分配器与像素分类 | 置信度计算、块级预算分配、状态机更新 | 0.2 ms |
| 光线追踪采样 | 按分配结果执行 RT 查询 | 0.6 ms |
| 空间滤波 | A-trous 分解重构、状态边界保护 | 0.4 ms |
| 时域分享 | 重投影、历史混合、统计更新 | 0.3 ms |
| 修复后处理 | 状态冷却、边缘修复 | 0.1 ms |
总计在 1.6 ms 左右。相比纯 SVGF 方案,ATAA 多花了分配器的 0.2 ms,省下了空间滤波均匀铺开时浪费的大量带宽,整体收益是正的。在 4K 分辨率下,带宽压力会明显上升,我遇到过的主要瓶颈是两个统计缓冲同时读取时缓存命中率下降,所以建议把历史方差和置信度打包进同一张纹理,或者用交替采样减少随机访问。
4.3 效果验证:从三个维度看降噪质量
评价 ATAA 的效果,不能只看单帧截图。我建议每轮改动都抓三类证据:静止相机下的时间序列截图、运动相机下的视频段落、以及边缘细节的放大对比。
静止序列用来评估噪声收敛速度。一个理想结果应该是在 10 到 20 帧内,平坦区域方差降到几乎不可感知,高光反射边缘在 30 帧左右稳定。如果超过 60 帧还有明显闪烁,大概率是分配器没有给高方差区域足够样本,或者状态 A 的 alpha 收敛太慢。
运动序列用来评估残影和模糊。拿一个带高光反射的物体做平移,ATAA 在状态 B 区域应该保持轮廓锐利,允许轻微的时间平滑,但不应该出现半透明残影。如果出现残影,第一反应不是调低所有 alpha,而是看状态机是否把大量运动边缘误判成了状态 A。只要状态判断正确,alpha 本身不用动。
边缘细节对比则能看出空间滤波和时域积累之间的平衡。理想状态是几何硬边缘和材质高光边缘都能保持分离,不互相污染。如果高光边缘出现一圈模糊黑边,多半是空间滤波核越过了状态边界,或者深度阈值太敏感。把状态图可视化叠加到画面上,基本一眼就能看出滤波核是不是跨到了错误区域。
4.4 调试中的常见坑位与排查方法
我整理了几个调试 ATAA 过程中最常遇到的问题,每个都几乎必踩一次。
第一个是闪烁冒烟点。现象是静止画面里某个区域持续有亮点跳动,像烧开的水泡。排查思路是看该区域在状态机上处于什么状态。如果是状态 A 还闪,说明置信度误判了,历史没有被有效利用;如果是状态 C 还闪,说明分配器给你的 SPP 还不够,或者空间滤波被状态边界挡住了,没有机会“帮一把”。我的处理是给状态 C 加一个最低 SPP 保障,同时把置信度可视化打开,看是否是材质 ID 差异过大导致误判。
第二个是拖影和残影。前面说过,最常被忽略的原因是状态切换没有滞后。物体边界沿线像素如果每帧在状态 A 和状态 B 之间抖动,历史混合的时机就会错乱,形成类似拖影的效果。解决方法我给两档:先加两帧以上的状态冷却,让状态切换更保守;如果还拖,再检查运动矢量本身,特别是快速旋转时运动矢量是否只反映了位移而漏掉了旋转分量。我遇到过摄像机快速转动时,像素速度方向正确但长度偏小,导致历史重投影位置落后一两个像素,整个画面边缘都有“小尾巴”。修正方式是让运动矢量在生成时额外包含相机旋转项。
第三个是高光边缘的黑色描边。多数时候是空间滤波的颜色权重在对数空间和高光区域计算不一致导致的。我建议在 A-trous 滤波前先对亮度求局部均值,滤波权重里同时考虑相对亮度和绝对亮度,并且给高亮像素放宽颜色差异阈值。这个修正是纯参数层面的,调起来比较靠经验,但可视化权重图以后会清晰很多。
第四个是运动物体的过度模糊。现象是人物快速移动时,整个角色轮廓变得不清,像被涂抹过。常见原因有两个:要么状态 B 的 alpha 太低,时域平滑过度;要么是分配器在运动物体内部没有触发额外采样,SPP 不足导致空间滤波需要大半径来压噪,结果把轮廓磨掉了。你可以先把空间滤波半径缩小三分之一,看轮廓是否恢复,如果恢复就说明空间滤波强度过高。
第五个是整个画面在光照突变时变暗或变亮。这是历史置信度衰减不够导致的。比如室内突然打开一盏灯,时间积累较长的像素会保持旧亮度,需要几十帧才反应过来。调快置信度衰减能缩短反应时间,但会牺牲一点静止画面的降噪效果,是个取舍。我的做法是检测全局场景亮度变化率,当变化率超过阈值时,对全屏置信度做一个额外衰减,这样既能感知突发光照变化,又不会在平时削弱积累效果。
4.5 与超分和神经降噪结合的一点探索
ATAA 并不是终点。在测试过程中,我还尝试过把它输出结果喂给厂商自带的超分方案,效果比传统降噪后直接超分好一些。原因也好理解:ATAA 输出的历史统计信息已经带有置信度标记,超分方案在做画面重建时可以少花力气去猜“这个边缘是噪声还是细节”。
如果再往上一步,可以把 ATAA 的置信度图作为神经降噪器的额外输入通道。神经网络擅长处理模糊的纹理重建,但不擅长处理时域不连续,而置信度图正好提供了“当前这个位置时间累积到第几帧、可不可信”的先验信息。我测试过一个轻量级网络,把状态和置信度图作为额外输入,整体 PSNR 提升有限,但主观视觉的闪烁明显减少。说明置信度信息确实在帮助网络做时间一致性判断。
如果你要自己扩展 ATAA,我认为最有价值的方向是让分配器具备预测能力:根据当前帧的方差变化趋势,提前预判下一帧哪些区域会退化,然后提前分配采样预算。这相当于给基本的三状态机加了一个速度项,让状态切换不再只是事后响应,而是带着一点“预判”去做时域积累。目前这只是初步实验,但方向我很看好。
写在最后的调试心得
这篇文章写到这,正文部分已经足够展开 ATAA 的全貌了。最后分享一点个人调试体会:如果你准备自己实现这套方案,最先做的不是把滤波核写得完美,而是把状态机和置信度图的可视化通道先搭出来。我一开始直接上手调 alpha 和滤波半径,结果花了两个晚上才找到闪烁来自状态抖动,而可视化面板做出来之后,很多问题一眼就能定位。另一个容易被低估的坑是历史缓冲的格式精度,一开始我用 RGBA8 存历史颜色,在暗部出现明显的色带和积累不均匀,换成 RGBA16F 后瞬间好转。这些经验都不在论文里写,但工程实现中往往比算法公式更致命。希望这篇能帮你少走几回弯路。