feat: ACES tonemap + Bloom 后处理链(Extract/BlurH/BlurV 半分辨率)
- ACES(Narkowicz) 经 push 开关启用,exposure 接 ImGui 滑条 - RG: TextureDesc 支持 sizeScale 相对尺寸;per-pass 渲染尺寸解析 (半分辨率 pass 的 viewport 由此决定) - RG Toposort 重写:RAW+WAR+WAW 按声明序逐资源建边,支持多写者 ping-pong 资源(旧逻辑对 reader 连接所有 writer,ping-pong 成环) - GetPhysicalView 缺物理镜像时 throw(ENGINE_FATAL 只记日志不中止) - 全屏 pass 通用 16B push + 公共录制 lambda,Extract/Blur/Tonemap 复用
This commit is contained in:
@@ -56,6 +56,16 @@ namespace Engine
|
||||
constexpr u32 kShadowMapSize = 2048;
|
||||
constexpr u32 kShadowPushSize = static_cast<u32>(2 * sizeof(glm::mat4));
|
||||
|
||||
// 全屏 pass 通用 push(16B):所有全屏 pass(Tonemap/Bloom 系)
|
||||
// 统一声明与推送,规避 VUID-08878 的"range 定义必须一致"
|
||||
struct FullscreenPush {
|
||||
f32 exposure;
|
||||
f32 param0; // 各 pass 自定语义(tonemap: aces 开关)
|
||||
f32 param1;
|
||||
f32 param2;
|
||||
};
|
||||
constexpr u32 kFullscreenPushSize = static_cast<u32>(sizeof(FullscreenPush));
|
||||
|
||||
// 光源正交包围盒(覆盖当前场景;后续按场景 AABB 自适应)
|
||||
constexpr f32 kLightOrthoExtent = 15.0f;
|
||||
constexpr f32 kLightDistance = 30.0f;
|
||||
@@ -147,16 +157,19 @@ namespace Engine
|
||||
.setCompareEnable(VK_FALSE);
|
||||
m_ShadowSampler = m_Device->GetDevice().createSampler(shadowSamplerInfo);
|
||||
|
||||
// 7.5 Tonemap pass 装配:Set0 = HDR 颜色图,push = {exposure}
|
||||
std::vector<DescriptorBinding> tonemapBindings = {
|
||||
// 7.5 全屏后处理装配(Tonemap + Bloom 共用一套 layout/sampler):
|
||||
// Set0 = {HDR 颜色图, Bloom 图},push = 16B 通用结构
|
||||
std::vector<DescriptorBinding> fullscreenBindings = {
|
||||
{0, vk::DescriptorType::eCombinedImageSampler,
|
||||
vk::ShaderStageFlagBits::eFragment},
|
||||
{1, vk::DescriptorType::eCombinedImageSampler,
|
||||
vk::ShaderStageFlagBits::eFragment},
|
||||
};
|
||||
m_TonemapSetLayout = m_DescriptorManager->CreateLayout(tonemapBindings);
|
||||
m_TonemapSetLayout = m_DescriptorManager->CreateLayout(fullscreenBindings);
|
||||
|
||||
m_TonemapPipelineLayout = m_DescriptorManager->CreatePipelineLayout(
|
||||
std::vector<vk::DescriptorSetLayout>{m_TonemapSetLayout},
|
||||
sizeof(f32));
|
||||
kFullscreenPushSize);
|
||||
|
||||
m_TonemapSampler = m_Device->GetDevice().createSampler(
|
||||
vk::SamplerCreateInfo{}
|
||||
@@ -170,17 +183,32 @@ namespace Engine
|
||||
auto tonemapVert = m_ShaderManager->CreateFromFile(
|
||||
shaderDir + "tonemap.vert.spv",
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
std::vector<vk::DescriptorSetLayout>{m_TonemapSetLayout}, sizeof(f32));
|
||||
std::vector<vk::DescriptorSetLayout>{m_TonemapSetLayout},
|
||||
kFullscreenPushSize);
|
||||
auto tonemapFrag = m_ShaderManager->CreateFromFile(
|
||||
shaderDir + "tonemap.frag.spv",
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
std::vector<vk::DescriptorSetLayout>{m_TonemapSetLayout}, sizeof(f32));
|
||||
std::vector<vk::DescriptorSetLayout>{m_TonemapSetLayout},
|
||||
kFullscreenPushSize);
|
||||
auto bloomExtractFrag = m_ShaderManager->CreateFromFile(
|
||||
shaderDir + "bloom_extract.frag.spv",
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
std::vector<vk::DescriptorSetLayout>{m_TonemapSetLayout},
|
||||
kFullscreenPushSize);
|
||||
auto bloomBlurFrag = m_ShaderManager->CreateFromFile(
|
||||
shaderDir + "bloom_blur.frag.spv",
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
std::vector<vk::DescriptorSetLayout>{m_TonemapSetLayout},
|
||||
kFullscreenPushSize);
|
||||
|
||||
m_TonemapVertShader = tonemapVert.Handle;
|
||||
m_TonemapFragShader = tonemapFrag.Handle;
|
||||
m_BloomExtractFrag = bloomExtractFrag.Handle;
|
||||
m_BloomBlurFrag = bloomBlurFrag.Handle;
|
||||
|
||||
if (!m_TonemapVertShader || !m_TonemapFragShader) {
|
||||
ENGINE_FATAL("Failed to create tonemap shader objects");
|
||||
if (!m_TonemapVertShader || !m_TonemapFragShader ||
|
||||
!m_BloomExtractFrag || !m_BloomBlurFrag) {
|
||||
ENGINE_FATAL("Failed to create fullscreen shader objects");
|
||||
}
|
||||
|
||||
// 8. Swapchain(只管呈现;深度图由 RenderGraph 创建)
|
||||
@@ -199,6 +227,9 @@ namespace Engine
|
||||
m_UBODescriptorSets.resize(kMaxFramesInFlight);
|
||||
m_ShadowDescriptorSets.resize(kMaxFramesInFlight);
|
||||
m_TonemapDescriptorSets.resize(kMaxFramesInFlight);
|
||||
m_BloomExtractSets.resize(kMaxFramesInFlight);
|
||||
m_BloomBlurHSets.resize(kMaxFramesInFlight);
|
||||
m_BloomBlurVSets.resize(kMaxFramesInFlight);
|
||||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||
m_UBODescriptorSets[slot] =
|
||||
m_DescriptorManager->AllocateDescriptorSet(m_UBOSetLayout);
|
||||
@@ -211,6 +242,13 @@ namespace Engine
|
||||
|
||||
m_TonemapDescriptorSets[slot] =
|
||||
m_DescriptorManager->AllocateDescriptorSet(m_TonemapSetLayout);
|
||||
|
||||
m_BloomExtractSets[slot] =
|
||||
m_DescriptorManager->AllocateDescriptorSet(m_TonemapSetLayout);
|
||||
m_BloomBlurHSets[slot] =
|
||||
m_DescriptorManager->AllocateDescriptorSet(m_TonemapSetLayout);
|
||||
m_BloomBlurVSets[slot] =
|
||||
m_DescriptorManager->AllocateDescriptorSet(m_TonemapSetLayout);
|
||||
}
|
||||
|
||||
// 11. RenderGraph:声明 + 编译(场景在 Render(Scene&) 时按帧绑定)
|
||||
@@ -218,7 +256,7 @@ namespace Engine
|
||||
|
||||
// Compile 已分配物理镜像:把各槽位的阴影图/HDR view 绑进描述符
|
||||
UpdateShadowDescriptors();
|
||||
UpdateTonemapDescriptors();
|
||||
UpdateFullscreenDescriptors();
|
||||
|
||||
ENGINE_INFO("Renderer initialized");
|
||||
}
|
||||
@@ -247,6 +285,12 @@ namespace Engine
|
||||
if (m_TonemapFragShader) {
|
||||
vkDevice.destroyShaderEXT(m_TonemapFragShader);
|
||||
}
|
||||
if (m_BloomExtractFrag) {
|
||||
vkDevice.destroyShaderEXT(m_BloomExtractFrag);
|
||||
}
|
||||
if (m_BloomBlurFrag) {
|
||||
vkDevice.destroyShaderEXT(m_BloomBlurFrag);
|
||||
}
|
||||
|
||||
vkDevice.destroySampler(m_ShadowSampler);
|
||||
vkDevice.destroySampler(m_TonemapSampler);
|
||||
@@ -300,6 +344,14 @@ namespace Engine
|
||||
"HDRColor",
|
||||
RG::TextureDesc{vk::Format::eR16G16B16A16Sfloat});
|
||||
|
||||
// Bloom ping-pong:半分辨率(sizeScale 相对目标尺寸,resize 自动跟随)
|
||||
m_BloomA = m_RenderGraph->CreateTexture(
|
||||
"BloomA",
|
||||
RG::TextureDesc{vk::Format::eR16G16B16A16Sfloat, 0, 0, 0.5f});
|
||||
m_BloomB = m_RenderGraph->CreateTexture(
|
||||
"BloomB",
|
||||
RG::TextureDesc{vk::Format::eR16G16B16A16Sfloat, 0, 0, 0.5f});
|
||||
|
||||
// ---- Shadow pass:光源视角写深度(所有渲染体都写入,不做实体级开关——
|
||||
// castShadow 应由材质/实体数据驱动剔除,而非 pass 逻辑特判)----
|
||||
m_RenderGraph->AddPass("Shadow",
|
||||
@@ -527,11 +579,12 @@ namespace Engine
|
||||
.Read(m_ShadowMap, RG::ResourceUse::ShaderRead,
|
||||
vk::ShaderStageFlagBits::eFragment);
|
||||
|
||||
// ---- Tonemap pass:HDR → 显示空间,全屏三角无顶点缓冲 ----
|
||||
// 显示变换在此显式建模(此前隐式依赖 Srgb 附件,是设计欠账)。
|
||||
// Backbuffer 从此只接收"显示空间"颜色:tonemap 直接写、ImGui 叠加。
|
||||
m_RenderGraph->AddPass("Tonemap",
|
||||
[this](RG::RenderGraphContext& ctx) {
|
||||
// ---- 全屏 pass 公共录制:动态状态/draw 全一致,仅附件 view、
|
||||
// 输入 set、push 值不同(Extract/BlurH/BlurV/Tonemap 复用)----
|
||||
auto recordFullscreen =
|
||||
[this](RG::RenderGraphContext& ctx, vk::ImageView view,
|
||||
vk::ShaderEXT frag, const vk::DescriptorSet& set,
|
||||
const FullscreenPush& push) {
|
||||
const vk::CommandBuffer cmd = ctx.cmd;
|
||||
|
||||
vk::ClearValue clearValue;
|
||||
@@ -539,7 +592,7 @@ namespace Engine
|
||||
std::array<float, 4>{0.0f, 0.0f, 0.0f, 1.0f});
|
||||
|
||||
vk::RenderingAttachmentInfo colorAttachment;
|
||||
colorAttachment.setImageView(ctx.View(m_Backbuffer))
|
||||
colorAttachment.setImageView(view)
|
||||
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
|
||||
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
||||
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
||||
@@ -557,12 +610,11 @@ namespace Engine
|
||||
vk::ShaderStageFlagBits::eFragment
|
||||
};
|
||||
std::array<vk::ShaderEXT, 2> shaders = {
|
||||
m_TonemapVertShader, m_TonemapFragShader};
|
||||
m_TonemapVertShader, frag};
|
||||
cmd.bindShadersEXT(stages, shaders);
|
||||
|
||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||
m_TonemapPipelineLayout, 0,
|
||||
m_TonemapDescriptorSets[ctx.frame.frameSlot], {});
|
||||
m_TonemapPipelineLayout, 0, set, {});
|
||||
|
||||
// ---- 动态状态(无深度附件、无顶点输入)----
|
||||
cmd.setRasterizerDiscardEnable(VK_FALSE);
|
||||
@@ -592,17 +644,65 @@ namespace Engine
|
||||
cmd.setViewportWithCount(viewport);
|
||||
cmd.setScissorWithCount(vk::Rect2D({0, 0}, ctx.extent));
|
||||
|
||||
const f32 exposure = 1.0f; // TODO: 接 ImGui 曝光滑条
|
||||
cmd.pushConstants(m_TonemapPipelineLayout,
|
||||
vk::ShaderStageFlagBits::eVertex |
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
0, sizeof(f32), &exposure);
|
||||
0, kFullscreenPushSize, &push);
|
||||
|
||||
cmd.draw(3, 1, 0, 0);
|
||||
cmd.endRendering();
|
||||
};
|
||||
|
||||
// ---- Bloom:Extract(HDR 软阈值)→ BlurH → BlurV,全部半分辨率 ----
|
||||
m_RenderGraph->AddPass("BloomExtract",
|
||||
[this, recordFullscreen](RG::RenderGraphContext& ctx) {
|
||||
recordFullscreen(ctx, ctx.View(m_BloomA), m_BloomExtractFrag,
|
||||
m_BloomExtractSets[ctx.frame.frameSlot],
|
||||
FullscreenPush{0.0f, m_BloomThreshold, 0.0f, 0.0f});
|
||||
})
|
||||
.Read(m_HDRColor, RG::ResourceUse::ShaderRead,
|
||||
vk::ShaderStageFlagBits::eFragment)
|
||||
.Write(m_BloomA, RG::ResourceUse::ColorAttachment);
|
||||
|
||||
m_RenderGraph->AddPass("BloomBlurH",
|
||||
[this, recordFullscreen](RG::RenderGraphContext& ctx) {
|
||||
recordFullscreen(ctx, ctx.View(m_BloomB), m_BloomBlurFrag,
|
||||
m_BloomBlurHSets[ctx.frame.frameSlot],
|
||||
FullscreenPush{0.0f,
|
||||
1.0f / ctx.extent.width, 0.0f,
|
||||
0.0f});
|
||||
})
|
||||
.Read(m_BloomA, RG::ResourceUse::ShaderRead,
|
||||
vk::ShaderStageFlagBits::eFragment)
|
||||
.Write(m_BloomB, RG::ResourceUse::ColorAttachment);
|
||||
|
||||
m_RenderGraph->AddPass("BloomBlurV",
|
||||
[this, recordFullscreen](RG::RenderGraphContext& ctx) {
|
||||
recordFullscreen(ctx, ctx.View(m_BloomA), m_BloomBlurFrag,
|
||||
m_BloomBlurVSets[ctx.frame.frameSlot],
|
||||
FullscreenPush{0.0f, 0.0f,
|
||||
1.0f / ctx.extent.height,
|
||||
0.0f});
|
||||
})
|
||||
.Read(m_BloomB, RG::ResourceUse::ShaderRead,
|
||||
vk::ShaderStageFlagBits::eFragment)
|
||||
.Write(m_BloomA, RG::ResourceUse::ColorAttachment);
|
||||
|
||||
// ---- Tonemap pass:HDR(+bloom)→ 显示空间,全屏三角无顶点缓冲 ----
|
||||
// 显示变换在此显式建模(此前隐式依赖 Srgb 附件,是设计欠账)。
|
||||
// Backbuffer 从此只接收"显示空间"颜色:tonemap 直接写、ImGui 叠加。
|
||||
m_RenderGraph->AddPass("Tonemap",
|
||||
[this, recordFullscreen](RG::RenderGraphContext& ctx) {
|
||||
recordFullscreen(ctx, ctx.View(m_Backbuffer), m_TonemapFragShader,
|
||||
m_TonemapDescriptorSets[ctx.frame.frameSlot],
|
||||
FullscreenPush{m_Exposure,
|
||||
m_UseACES ? 1.0f : 0.0f,
|
||||
m_BloomStrength, 0.0f});
|
||||
})
|
||||
.Write(m_Backbuffer, RG::ResourceUse::ColorAttachment)
|
||||
.Read(m_HDRColor, RG::ResourceUse::ShaderRead,
|
||||
vk::ShaderStageFlagBits::eFragment)
|
||||
.Read(m_BloomA, RG::ResourceUse::ShaderRead,
|
||||
vk::ShaderStageFlagBits::eFragment);
|
||||
|
||||
// ---- ImGui pass:叠加层(loadOp=Load,不清屏)----
|
||||
@@ -661,7 +761,7 @@ namespace Engine
|
||||
|
||||
// 重编译后物理镜像可能变化,描述符重新绑定
|
||||
UpdateShadowDescriptors();
|
||||
UpdateTonemapDescriptors();
|
||||
UpdateFullscreenDescriptors();
|
||||
}
|
||||
|
||||
void Renderer::UpdateShadowDescriptors()
|
||||
@@ -675,13 +775,32 @@ namespace Engine
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::UpdateTonemapDescriptors()
|
||||
void Renderer::UpdateFullscreenDescriptors()
|
||||
{
|
||||
// 全屏 pass 的输入绑定(per-slot 物理镜像):
|
||||
// Tonemap = {0: HDRColor, 1: BloomA}
|
||||
// Extract = {0: HDRColor} (binding1 不用,可不写)
|
||||
// BlurH = {0: BloomA}
|
||||
// BlurV = {0: BloomB}
|
||||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||
const vk::ImageView hdr =
|
||||
m_RenderGraph->GetPhysicalView(m_HDRColor, slot);
|
||||
const vk::ImageView bloomA =
|
||||
m_RenderGraph->GetPhysicalView(m_BloomA, slot);
|
||||
const vk::ImageView bloomB =
|
||||
m_RenderGraph->GetPhysicalView(m_BloomB, slot);
|
||||
|
||||
m_DescriptorManager->UpdateTexture(
|
||||
m_TonemapDescriptorSets[slot], 0,
|
||||
m_TonemapSampler,
|
||||
m_RenderGraph->GetPhysicalView(m_HDRColor, slot));
|
||||
m_TonemapDescriptorSets[slot], 0, m_TonemapSampler, hdr);
|
||||
m_DescriptorManager->UpdateTexture(
|
||||
m_TonemapDescriptorSets[slot], 1, m_TonemapSampler, bloomA);
|
||||
|
||||
m_DescriptorManager->UpdateTexture(
|
||||
m_BloomExtractSets[slot], 0, m_TonemapSampler, hdr);
|
||||
m_DescriptorManager->UpdateTexture(
|
||||
m_BloomBlurHSets[slot], 0, m_TonemapSampler, bloomA);
|
||||
m_DescriptorManager->UpdateTexture(
|
||||
m_BloomBlurVSets[slot], 0, m_TonemapSampler, bloomB);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user