DirectX 12实现TAA时运行数帧后像素颜色不更新
TAA实现后历史帧覆盖新帧、画面脏污问题修复
实现TAA时采用固定权重混合当前帧与历史帧,运行数帧后渲染结果仅显示历史像素内容,新帧无法正常叠加,画面出现脏污错误。
原有错误实现代码
float4 PS(VertexOut pin) : SV_Target { float2 currPos = pin.PosH.xy * gInvRenderTargetSize; float2 histPos = currPos + gVelocityBuffer.Sample(gsamPointClamp, currPos).xy; float4 currPixel = gCurrFrameMap.Sample(gsamPointClamp, currPos); float4 histPixel = gHistoryFrameMap.Sample(gsamPointClamp, histPos); if(gFirstFrame == 1) { return currPixel; } float4 taaPixel = 0.05 * currPixel + 0.95 * histPixel; return taaPixel; }
问题现象截图
- 渲染目标画面

- 历史帧画面

- 当前帧画面

- 像素无变化的渲染目标帧

问题根因
- 核心错误:未实现历史帧双缓冲轮转与回写。现有逻辑始终从固定的历史帧纹理采样,混合后的TAA结果从未被写入历史帧缓存,导致历史帧永远停留在第一帧的内容,在0.95的高历史权重下,运行数帧后新帧内容占比被持续压低,最终画面几乎只保留第一帧的历史像素。
- 坐标计算错误:速度向量存储的是当前帧像素与上一帧对应像素的位置差,现有代码用
currPos + velocity计算历史坐标,方向完全相反,采样到的历史像素和当前像素无对应关系,直接引发脏污拖影。 - 缺失容错逻辑:没有做历史坐标越界判断、历史颜色邻域钳制,速度向量采样错误、像素新入画面时会采样到无关内容,错误会逐帧累积。
- 权重设置僵化:固定0.05/0.95的混合权重没有根据像素运动状态、颜色差异动态调整,运动像素、新出现像素的历史权重过高,残影无法消除。
修复方案
- 配置历史帧双缓冲,实现正确的帧轮转
准备两张和渲染目标同分辨率、同格式的纹理作为历史帧缓存,每帧按如下逻辑执行:- 奇数帧:从历史缓存A读取上一帧的TAA结果,将当前帧与历史帧混合后的新TAA结果写入历史缓存B,同时将缓存B的内容作为当前帧输出到屏幕
- 偶数帧:交换读写角色,从历史缓存B读取上一帧结果,混合后写入历史缓存A,循环往复
第一帧无需混合,直接将当前帧内容写入对应历史缓存即可。
注意:历史帧纹理在采样、写入前必须切换到正确的资源状态,避免资源状态错误导致读取到旧数据。
- 修正历史坐标计算逻辑,增加越界判断
正确的历史UV坐标计算方式为当前坐标减去速度向量,采样前判断坐标是否在[0,1]的合法UV范围内,越界则直接使用当前帧像素,不混合历史:float2 velocity = gVelocityBuffer.Sample(gsamPointClamp, currPos).xy; float2 histPos = currPos - velocity; // 越界判断 if(histPos.x < 0.0f || histPos.x > 1.0f || histPos.y <0.0f || histPos.y>1.0f) { return currPixel; } float4 histPixel = gHistoryFrameMap.Sample(gsamPointClamp, histPos); - 增加历史颜色邻域钳制
对当前帧像素周围3x3邻域采样,计算邻域颜色的最小值、最大值,将采样到的历史像素颜色钳制到这个区间内,避免历史错误像素逐帧累积扩散。 - 动态调整混合权重
取消固定权重,根据像素运动幅度、当前帧与历史帧的颜色差异调整权重:静止、颜色差异小的像素保留0.90.95的高历史权重,保证抗锯齿效果;运动幅度大、颜色差异大、新入画面的像素将当前帧权重提高到0.20.5,加快响应速度,消除残影。
内容的提问来源于stack exchange,提问作者CarolGolang
相关产品推荐
相关产品推荐

