feat: TAA 时域抗锯齿(velocity + 邻域 clamp + 子像素抖动)

- Velocity buffer:Forward 第二颜色附件,vertex 计算 clip 空间运动向量
  (UBO 尾部追加 projNoJitter/prevView/prevProj;上一帧实例矩阵 SSBO
  Set0 binding2,与当前实例数组平行对齐——转动的物体速度正确不鬼影)
- 子像素抖动:SceneRenderer Halton(2,3)×8 序列,抖动只进光栅化投影
- TAA resolve pass:velocity 反推历史位置 + 3×3 邻域 clamp 去鬼影 +
  mix(cur,hist,0.9) 时域累积;TAAColor 自引用历史
- RG 历史读补 post-barrier:采样后恢复镜像 resting layout(TAA 的历史
  是 ColorAttachment layout,不恢复则下帧 entry oldLayout 脱节)
- 管线:Forward(HDR+Velocity) → TAA(TAAColor) → Bloom/Tonemap 改源 TAAColor
- 编译零错误、validation 零警告、8 秒冒烟通过(10 pass 全活跃)
This commit is contained in:
2026-09-23 13:29:31 +08:00
parent a29aed2803
commit 27b7f8785c
18 changed files with 369 additions and 39 deletions
+30
View File
@@ -177,6 +177,7 @@ namespace Engine::RG {
for (auto& pass : m_Passes) {
pass.culled = false;
pass.barriers.clear();
pass.postBarriers.clear();
}
for (auto& entry : m_Textures) ResolveUsage(entry);
@@ -468,6 +469,20 @@ namespace Engine::RG {
.setOldLayout(entry.restingLayout);
pass.barriers.push_back({use.resIndex, steady, first, true});
// 出口 barrier:采样完成后把历史镜像转回 resting layout。
// (若 desired == resting 则是无害的空转换,但保留它以
// 统一语义——src scope 同时给出读取完成的内存序)
vk::ImageMemoryBarrier2 exit;
exit.setSrcStageMask(dstStage)
.setSrcAccessMask(dstAccess)
.setDstStageMask(vk::PipelineStageFlagBits2::eNone)
.setDstAccessMask(vk::AccessFlagBits2::eNone)
.setOldLayout(desiredLayout)
.setNewLayout(entry.restingLayout)
.setSubresourceRange({entry.aspect, 0, 1, 0, 1});
pass.postBarriers.push_back({use.resIndex, exit, exit, true});
continue; // 不推进 chain:本帧链只描述当前槽位镜像
}
@@ -763,6 +778,21 @@ namespace Engine::RG {
pass.exec(ctx);
}
// 历史读出口:恢复上一帧槽位镜像的 resting layout
if (!pass.postBarriers.empty()) {
m_ScratchBarriers.clear();
for (const auto& bp : pass.postBarriers) {
auto& entry = m_Textures[bp.resIndex];
const u32 histSlot = HistorySlot(frame.frameSlot);
vk::ImageMemoryBarrier2 b = bp.steady;
b.setImage(entry.physical[histSlot]->image);
m_ScratchBarriers.push_back(b);
}
vk::DependencyInfo dep;
dep.setImageMemoryBarriers(m_ScratchBarriers);
frame.cmd.pipelineBarrier2(dep);
}
if (m_QueryPool) {
frame.cmd.writeTimestamp2(
vk::PipelineStageFlagBits2::eBottomOfPipe,