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:
@@ -28,6 +28,18 @@ namespace Engine
|
||||
constexpr f32 kLightDistance = 30.0f;
|
||||
constexpr f32 kLightNear = 1.0f;
|
||||
constexpr f32 kLightFar = 80.0f;
|
||||
|
||||
// Halton 低差异序列(TAA 子像素抖动)
|
||||
f32 Halton(u32 index, u32 base)
|
||||
{
|
||||
f32 f = 1.0f, r = 0.0f;
|
||||
while (index > 0) {
|
||||
f /= static_cast<f32>(base);
|
||||
r += f * static_cast<f32>(index % base);
|
||||
index /= base;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
void SceneRenderer::ComputeLightSpace(const glm::vec3& shadowDir)
|
||||
@@ -53,10 +65,15 @@ namespace Engine
|
||||
}
|
||||
|
||||
bool SceneRenderer::BuildFrame(ECS::Scene& scene, Assets::AssetManager& assets,
|
||||
f32 aspect)
|
||||
vk::Extent2D targetExtent)
|
||||
{
|
||||
const auto collectStart = std::chrono::steady_clock::now();
|
||||
|
||||
// ---- 0. TAA 时域状态先行(velocity = 上一帧姿态与相机)----
|
||||
UniformBufferObject& ubo = m_Frame.ubo;
|
||||
ubo.prevView = m_LastView;
|
||||
ubo.prevProj = m_LastProjNoJitter;
|
||||
|
||||
// ---- 1. 相机 ----
|
||||
auto* cameraComp = scene.GetPrimaryCamera();
|
||||
if (!cameraComp) {
|
||||
@@ -65,9 +82,24 @@ namespace Engine
|
||||
}
|
||||
const Camera& camera = cameraComp->camera;
|
||||
|
||||
UniformBufferObject& ubo = m_Frame.ubo;
|
||||
const f32 aspect = static_cast<f32>(targetExtent.width) /
|
||||
static_cast<f32>(targetExtent.height);
|
||||
ubo.view = camera.GetView();
|
||||
ubo.proj = camera.GetProjection(aspect, 0.1f, 100.0f);
|
||||
|
||||
// 未抖动投影:velocity 的当前帧 clip 位置用它(抖动不能进 velocity)
|
||||
ubo.projNoJitter = ubo.proj;
|
||||
|
||||
// 子像素抖动(Halton×8):光栅化采样位置逐帧偏移,TAA 累积去锯齿。
|
||||
// NDC 偏移 = 像素偏移 × 2 / 尺寸
|
||||
{
|
||||
const u32 idx = static_cast<u32>(m_FrameIndex % 8);
|
||||
const f32 jx = Halton(idx + 1, 2) - 0.5f;
|
||||
const f32 jy = Halton(idx + 1, 3) - 0.5f;
|
||||
ubo.proj[2][0] += jx * 2.0f / static_cast<f32>(targetExtent.width);
|
||||
ubo.proj[2][1] += jy * 2.0f / static_cast<f32>(targetExtent.height);
|
||||
}
|
||||
|
||||
ubo.ambientColor = scene.GetSettings().ambientColor;
|
||||
|
||||
// ---- 2. 灯光(第一个灯作为阴影光源)----
|
||||
@@ -98,6 +130,7 @@ namespace Engine
|
||||
// 实例矩阵数组 = [主视图段 | 阴影视图段],batch.first 为全局下标。
|
||||
// 哈希聚合保证各视图内 batch 区间连续。
|
||||
m_Frame.instanceModels.clear();
|
||||
m_Frame.prevInstanceModels.clear();
|
||||
m_Frame.mainView.batches.clear();
|
||||
m_Frame.shadowView.batches.clear();
|
||||
m_Frame.mainView.instanceCount = 0;
|
||||
@@ -112,10 +145,19 @@ namespace Engine
|
||||
const Frustum shadowFrustum = Frustum::FromMatrix(ubo.lightSpace);
|
||||
|
||||
scene.Each<TransformComponent, MeshRendererComponent>(
|
||||
[&](Entity, TransformComponent& t, MeshRendererComponent& mr) {
|
||||
[&](Entity entity, TransformComponent& t, MeshRendererComponent& mr) {
|
||||
Model* model = assets.GetModel(mr.model);
|
||||
if (!model) return;
|
||||
|
||||
// TAA velocity:本实例的上一帧世界矩阵(首帧/新实体 = 当前
|
||||
// 矩阵 → 速度为 0,不产生假运动)
|
||||
const u32 entKey = static_cast<u32>(entt::to_integral(entity));
|
||||
glm::mat4 prevWorld = t.world;
|
||||
if (auto it = m_PrevWorlds.find(entKey); it != m_PrevWorlds.end()) {
|
||||
prevWorld = it->second;
|
||||
}
|
||||
m_CurWorlds.emplace(entKey, t.world);
|
||||
|
||||
// 实例世界缩放(矩阵列向量最大长度,包围球半径随缩放)
|
||||
const f32 worldScale = glm::max(
|
||||
glm::length(glm::vec3(t.world[0])),
|
||||
@@ -145,7 +187,9 @@ namespace Engine
|
||||
}
|
||||
view.batches[it->second].count++;
|
||||
view.instanceCount++;
|
||||
// 两个数组平行 push:同下标 = 同一实例的当前/上一帧姿态
|
||||
m_Frame.instanceModels.push_back(t.world);
|
||||
m_Frame.prevInstanceModels.push_back(prevWorld);
|
||||
};
|
||||
|
||||
tryAppend(m_Frame.mainView, mainFrustum, m_MainLookup);
|
||||
@@ -153,6 +197,13 @@ namespace Engine
|
||||
}
|
||||
});
|
||||
|
||||
// 帧末滚动时域状态:cur → prev(每帧重建,销毁的实体自动清出)
|
||||
m_PrevWorlds.swap(m_CurWorlds);
|
||||
m_CurWorlds.clear();
|
||||
m_LastView = ubo.view;
|
||||
m_LastProjNoJitter = ubo.projNoJitter;
|
||||
++m_FrameIndex;
|
||||
|
||||
if (instanceTruncated && !m_TruncationWarned) {
|
||||
// 只告警一次(每帧告警会刷屏)
|
||||
m_TruncationWarned = true;
|
||||
|
||||
Reference in New Issue
Block a user