feat: HDR 管线 + Tonemap pass,显示变换显式建模
管线:Shadow → Forward(写 HDRColor R16G16B16A16F) → Tonemap → ImGui(loadOp=Load) → Present。对齐 UE/Unity 结构:场景线性值 进 HDR 离屏图,Tonemap(全屏三角,exposure push + gamma 编码) 显式完成显示变换,Backbuffer 只接收显示空间颜色。 - Swapchain 改 B8G8R8A8Unorm:Srgb 附件会硬件二次提亮,UI 泛白 (#4296FA→#8CCBFC);Unorm 下写什么显示什么,编码责任归 Tonemap - Shader object push range 规则落地:程序级声明——同一 pass 的 全部 shader EXT range 定义必须一致(VUID-08878),layout 同样 按 vertex|fragment 声明,push 调用点覆盖全部声明 stage(VUID-01796) - Tonemap 装配:Set0 = HDR 图(per-frame-slot 描述符 + GetPhysicalView), clamp 占位待 ACES 替换,bloom/FXAA 等后处理在 HDR 域插 pass - CMake 加 imgui_demo.cpp(ShowDemoWindow 链接) - 修复:m_TonemapDescriptorSets 未 resize 即下标写入(启动即崩) 验证:5 pass 全活,validation 0 error 0 warning,UI 原生色彩无泛白
This commit is contained in:
@@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
#include "Engine/ImGui/ImGuiLayer.h"
|
||||||
|
|
||||||
using namespace Engine;
|
using namespace Engine;
|
||||||
using namespace Engine::ECS;
|
using namespace Engine::ECS;
|
||||||
|
|
||||||
@@ -114,6 +116,8 @@ protected:
|
|||||||
{
|
{
|
||||||
if (!m_OverlayVisible) return;
|
if (!m_OverlayVisible) return;
|
||||||
|
|
||||||
|
ImGui::ShowDemoWindow();
|
||||||
|
|
||||||
ImGui::Begin("Scene Debug");
|
ImGui::Begin("Scene Debug");
|
||||||
|
|
||||||
// 阴影光源方向:拖动滑条实时看影子变化
|
// 阴影光源方向:拖动滑条实时看影子变化
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ set(IMGUI_SOURCES
|
|||||||
${IMGUI_DIR}/imgui_draw.cpp
|
${IMGUI_DIR}/imgui_draw.cpp
|
||||||
${IMGUI_DIR}/imgui_tables.cpp
|
${IMGUI_DIR}/imgui_tables.cpp
|
||||||
${IMGUI_DIR}/imgui_widgets.cpp
|
${IMGUI_DIR}/imgui_widgets.cpp
|
||||||
|
${IMGUI_DIR}/imgui_demo.cpp # ShowDemoWindow 的实现(调试期保留)
|
||||||
${IMGUI_DIR}/backends/imgui_impl_glfw.cpp
|
${IMGUI_DIR}/backends/imgui_impl_glfw.cpp
|
||||||
${IMGUI_DIR}/backends/imgui_impl_vulkan.cpp
|
${IMGUI_DIR}/backends/imgui_impl_vulkan.cpp
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ namespace Engine {
|
|||||||
void BuildGraph(); // 声明 pass 结构 + 首次 Compile
|
void BuildGraph(); // 声明 pass 结构 + 首次 Compile
|
||||||
void HandleResize();
|
void HandleResize();
|
||||||
void UpdateShadowDescriptors();
|
void UpdateShadowDescriptors();
|
||||||
|
void UpdateTonemapDescriptors();
|
||||||
|
|
||||||
Window& m_Window;
|
Window& m_Window;
|
||||||
|
|
||||||
@@ -96,10 +97,19 @@ namespace Engine {
|
|||||||
vk::ShaderEXT m_ShadowVertShader{};
|
vk::ShaderEXT m_ShadowVertShader{};
|
||||||
vk::ShaderEXT m_ShadowFragShader{};
|
vk::ShaderEXT m_ShadowFragShader{};
|
||||||
|
|
||||||
|
// Tonemap pass 装配:Set0 = HDR 颜色图,push = {exposure}(fragment)
|
||||||
|
vk::PipelineLayout m_TonemapPipelineLayout;
|
||||||
|
vk::DescriptorSetLayout m_TonemapSetLayout;
|
||||||
|
std::vector<vk::DescriptorSet> m_TonemapDescriptorSets; // 每帧槽位一个
|
||||||
|
vk::Sampler m_TonemapSampler;
|
||||||
|
vk::ShaderEXT m_TonemapVertShader{};
|
||||||
|
vk::ShaderEXT m_TonemapFragShader{};
|
||||||
|
|
||||||
// ---- RenderGraph 资源句柄 ----
|
// ---- RenderGraph 资源句柄 ----
|
||||||
RG::TextureHandle m_Backbuffer{};
|
RG::TextureHandle m_Backbuffer{};
|
||||||
RG::TextureHandle m_Depth{};
|
RG::TextureHandle m_Depth{};
|
||||||
RG::TextureHandle m_ShadowMap{};
|
RG::TextureHandle m_ShadowMap{};
|
||||||
|
RG::TextureHandle m_HDRColor{}; // Forward 输出(线性 HDR),Tonemap 归一
|
||||||
|
|
||||||
// ---- 当帧绑定(Render() 期间有效,供 pass 回调读取)----
|
// ---- 当帧绑定(Render() 期间有效,供 pass 回调读取)----
|
||||||
ECS::Scene* m_BoundScene = nullptr;
|
ECS::Scene* m_BoundScene = nullptr;
|
||||||
|
|||||||
@@ -42,7 +42,10 @@ namespace Engine {
|
|||||||
vk::PipelineLayout CreatePipelineLayout(vk::DescriptorSetLayout layout,
|
vk::PipelineLayout CreatePipelineLayout(vk::DescriptorSetLayout layout,
|
||||||
u32 pushConstantSize);
|
u32 pushConstantSize);
|
||||||
|
|
||||||
// 多 set layout 的新接口
|
// 多 set layout 的新接口。
|
||||||
|
// push constant range 按程序级 vertex|fragment 声明(与
|
||||||
|
// ShaderManager 的 shader EXT 声明一致,VUID-08878);
|
||||||
|
// push 调用点必须带同样的双 stage(VUID-01796)
|
||||||
vk::PipelineLayout CreatePipelineLayout(
|
vk::PipelineLayout CreatePipelineLayout(
|
||||||
const std::vector<vk::DescriptorSetLayout>& layouts,
|
const std::vector<vk::DescriptorSetLayout>& layouts,
|
||||||
u32 pushConstantSize);
|
u32 pushConstantSize);
|
||||||
|
|||||||
@@ -147,6 +147,42 @@ namespace Engine
|
|||||||
.setCompareEnable(VK_FALSE);
|
.setCompareEnable(VK_FALSE);
|
||||||
m_ShadowSampler = m_Device->GetDevice().createSampler(shadowSamplerInfo);
|
m_ShadowSampler = m_Device->GetDevice().createSampler(shadowSamplerInfo);
|
||||||
|
|
||||||
|
// 7.5 Tonemap pass 装配:Set0 = HDR 颜色图,push = {exposure}
|
||||||
|
std::vector<DescriptorBinding> tonemapBindings = {
|
||||||
|
{0, vk::DescriptorType::eCombinedImageSampler,
|
||||||
|
vk::ShaderStageFlagBits::eFragment},
|
||||||
|
};
|
||||||
|
m_TonemapSetLayout = m_DescriptorManager->CreateLayout(tonemapBindings);
|
||||||
|
|
||||||
|
m_TonemapPipelineLayout = m_DescriptorManager->CreatePipelineLayout(
|
||||||
|
std::vector<vk::DescriptorSetLayout>{m_TonemapSetLayout},
|
||||||
|
sizeof(f32));
|
||||||
|
|
||||||
|
m_TonemapSampler = m_Device->GetDevice().createSampler(
|
||||||
|
vk::SamplerCreateInfo{}
|
||||||
|
.setMagFilter(vk::Filter::eLinear)
|
||||||
|
.setMinFilter(vk::Filter::eLinear)
|
||||||
|
.setMipmapMode(vk::SamplerMipmapMode::eNearest)
|
||||||
|
.setAddressModeU(vk::SamplerAddressMode::eClampToEdge)
|
||||||
|
.setAddressModeV(vk::SamplerAddressMode::eClampToEdge)
|
||||||
|
.setAddressModeW(vk::SamplerAddressMode::eClampToEdge));
|
||||||
|
|
||||||
|
auto tonemapVert = m_ShaderManager->CreateFromFile(
|
||||||
|
shaderDir + "tonemap.vert.spv",
|
||||||
|
vk::ShaderStageFlagBits::eVertex,
|
||||||
|
std::vector<vk::DescriptorSetLayout>{m_TonemapSetLayout}, sizeof(f32));
|
||||||
|
auto tonemapFrag = m_ShaderManager->CreateFromFile(
|
||||||
|
shaderDir + "tonemap.frag.spv",
|
||||||
|
vk::ShaderStageFlagBits::eFragment,
|
||||||
|
std::vector<vk::DescriptorSetLayout>{m_TonemapSetLayout}, sizeof(f32));
|
||||||
|
|
||||||
|
m_TonemapVertShader = tonemapVert.Handle;
|
||||||
|
m_TonemapFragShader = tonemapFrag.Handle;
|
||||||
|
|
||||||
|
if (!m_TonemapVertShader || !m_TonemapFragShader) {
|
||||||
|
ENGINE_FATAL("Failed to create tonemap shader objects");
|
||||||
|
}
|
||||||
|
|
||||||
// 8. Swapchain(只管呈现;深度图由 RenderGraph 创建)
|
// 8. Swapchain(只管呈现;深度图由 RenderGraph 创建)
|
||||||
m_Swapchain = CreateScope<Swapchain>(*m_Device, *m_Allocator, m_Window);
|
m_Swapchain = CreateScope<Swapchain>(*m_Device, *m_Allocator, m_Window);
|
||||||
m_Extent = m_Swapchain->GetExtent();
|
m_Extent = m_Swapchain->GetExtent();
|
||||||
@@ -162,6 +198,7 @@ namespace Engine
|
|||||||
|
|
||||||
m_UBODescriptorSets.resize(kMaxFramesInFlight);
|
m_UBODescriptorSets.resize(kMaxFramesInFlight);
|
||||||
m_ShadowDescriptorSets.resize(kMaxFramesInFlight);
|
m_ShadowDescriptorSets.resize(kMaxFramesInFlight);
|
||||||
|
m_TonemapDescriptorSets.resize(kMaxFramesInFlight);
|
||||||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||||
m_UBODescriptorSets[slot] =
|
m_UBODescriptorSets[slot] =
|
||||||
m_DescriptorManager->AllocateDescriptorSet(m_UBOSetLayout);
|
m_DescriptorManager->AllocateDescriptorSet(m_UBOSetLayout);
|
||||||
@@ -171,13 +208,17 @@ namespace Engine
|
|||||||
|
|
||||||
m_ShadowDescriptorSets[slot] =
|
m_ShadowDescriptorSets[slot] =
|
||||||
m_DescriptorManager->AllocateDescriptorSet(m_ShadowSetLayout);
|
m_DescriptorManager->AllocateDescriptorSet(m_ShadowSetLayout);
|
||||||
|
|
||||||
|
m_TonemapDescriptorSets[slot] =
|
||||||
|
m_DescriptorManager->AllocateDescriptorSet(m_TonemapSetLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 11. RenderGraph:声明 + 编译(场景在 Render(Scene&) 时按帧绑定)
|
// 11. RenderGraph:声明 + 编译(场景在 Render(Scene&) 时按帧绑定)
|
||||||
BuildGraph();
|
BuildGraph();
|
||||||
|
|
||||||
// Compile 已分配物理镜像:把各槽位的阴影图 view 绑进描述符
|
// Compile 已分配物理镜像:把各槽位的阴影图/HDR view 绑进描述符
|
||||||
UpdateShadowDescriptors();
|
UpdateShadowDescriptors();
|
||||||
|
UpdateTonemapDescriptors();
|
||||||
|
|
||||||
ENGINE_INFO("Renderer initialized");
|
ENGINE_INFO("Renderer initialized");
|
||||||
}
|
}
|
||||||
@@ -200,13 +241,22 @@ namespace Engine
|
|||||||
if (m_ShadowFragShader) {
|
if (m_ShadowFragShader) {
|
||||||
vkDevice.destroyShaderEXT(m_ShadowFragShader);
|
vkDevice.destroyShaderEXT(m_ShadowFragShader);
|
||||||
}
|
}
|
||||||
|
if (m_TonemapVertShader) {
|
||||||
|
vkDevice.destroyShaderEXT(m_TonemapVertShader);
|
||||||
|
}
|
||||||
|
if (m_TonemapFragShader) {
|
||||||
|
vkDevice.destroyShaderEXT(m_TonemapFragShader);
|
||||||
|
}
|
||||||
|
|
||||||
vkDevice.destroySampler(m_ShadowSampler);
|
vkDevice.destroySampler(m_ShadowSampler);
|
||||||
|
vkDevice.destroySampler(m_TonemapSampler);
|
||||||
vkDevice.destroyPipelineLayout(m_PipelineLayout);
|
vkDevice.destroyPipelineLayout(m_PipelineLayout);
|
||||||
vkDevice.destroyPipelineLayout(m_ShadowPipelineLayout);
|
vkDevice.destroyPipelineLayout(m_ShadowPipelineLayout);
|
||||||
|
vkDevice.destroyPipelineLayout(m_TonemapPipelineLayout);
|
||||||
vkDevice.destroyDescriptorSetLayout(m_UBOSetLayout);
|
vkDevice.destroyDescriptorSetLayout(m_UBOSetLayout);
|
||||||
vkDevice.destroyDescriptorSetLayout(m_TextureSetLayout);
|
vkDevice.destroyDescriptorSetLayout(m_TextureSetLayout);
|
||||||
vkDevice.destroyDescriptorSetLayout(m_ShadowSetLayout);
|
vkDevice.destroyDescriptorSetLayout(m_ShadowSetLayout);
|
||||||
|
vkDevice.destroyDescriptorSetLayout(m_TonemapSetLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::Format Renderer::GetSwapchainFormat() const
|
vk::Format Renderer::GetSwapchainFormat() const
|
||||||
@@ -243,6 +293,13 @@ namespace Engine
|
|||||||
"ShadowMap",
|
"ShadowMap",
|
||||||
RG::TextureDesc{vk::Format::eD32Sfloat, kShadowMapSize, kShadowMapSize});
|
RG::TextureDesc{vk::Format::eD32Sfloat, kShadowMapSize, kShadowMapSize});
|
||||||
|
|
||||||
|
// HDR 颜色图:Forward 的输出(线性光照值),Tonemap 显式完成
|
||||||
|
// 显示变换后写入 Backbuffer。对齐 UE/Unity 管线:
|
||||||
|
// SceneColor(HDR) → tonemapper → UI(UI 在 tonemap 之后叠加)
|
||||||
|
m_HDRColor = m_RenderGraph->CreateTexture(
|
||||||
|
"HDRColor",
|
||||||
|
RG::TextureDesc{vk::Format::eR16G16B16A16Sfloat});
|
||||||
|
|
||||||
// ---- Shadow pass:光源视角写深度(所有渲染体都写入,不做实体级开关——
|
// ---- Shadow pass:光源视角写深度(所有渲染体都写入,不做实体级开关——
|
||||||
// castShadow 应由材质/实体数据驱动剔除,而非 pass 逻辑特判)----
|
// castShadow 应由材质/实体数据驱动剔除,而非 pass 逻辑特判)----
|
||||||
m_RenderGraph->AddPass("Shadow",
|
m_RenderGraph->AddPass("Shadow",
|
||||||
@@ -321,8 +378,10 @@ namespace Engine
|
|||||||
if (!model) return;
|
if (!model) return;
|
||||||
|
|
||||||
const ShadowPush push{m_LightSpace, transform.world};
|
const ShadowPush push{m_LightSpace, transform.world};
|
||||||
|
// stage 必须覆盖 layout range 声明的全部 stage(VUID-01796)
|
||||||
cmd.pushConstants(m_ShadowPipelineLayout,
|
cmd.pushConstants(m_ShadowPipelineLayout,
|
||||||
vk::ShaderStageFlagBits::eVertex,
|
vk::ShaderStageFlagBits::eVertex |
|
||||||
|
vk::ShaderStageFlagBits::eFragment,
|
||||||
0, kShadowPushSize, &push);
|
0, kShadowPushSize, &push);
|
||||||
|
|
||||||
for (auto& mesh : model->GetMeshes()) {
|
for (auto& mesh : model->GetMeshes()) {
|
||||||
@@ -345,7 +404,7 @@ namespace Engine
|
|||||||
std::array<float, 4>{0.1f, 0.1f, 0.1f, 1.0f});
|
std::array<float, 4>{0.1f, 0.1f, 0.1f, 1.0f});
|
||||||
|
|
||||||
vk::RenderingAttachmentInfo colorAttachment;
|
vk::RenderingAttachmentInfo colorAttachment;
|
||||||
colorAttachment.setImageView(ctx.View(m_Backbuffer))
|
colorAttachment.setImageView(ctx.View(m_HDRColor))
|
||||||
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
|
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
|
||||||
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
||||||
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
||||||
@@ -447,8 +506,10 @@ namespace Engine
|
|||||||
Model* model = m_BoundAssets->GetModel(meshRenderer.model);
|
Model* model = m_BoundAssets->GetModel(meshRenderer.model);
|
||||||
if (!model) return;
|
if (!model) return;
|
||||||
|
|
||||||
|
// stage 必须覆盖 layout range 声明的全部 stage(VUID-01796)
|
||||||
cmd.pushConstants(m_PipelineLayout,
|
cmd.pushConstants(m_PipelineLayout,
|
||||||
vk::ShaderStageFlagBits::eVertex,
|
vk::ShaderStageFlagBits::eVertex |
|
||||||
|
vk::ShaderStageFlagBits::eFragment,
|
||||||
0, sizeof(glm::mat4), &transform.world);
|
0, sizeof(glm::mat4), &transform.world);
|
||||||
|
|
||||||
for (auto& mesh : model->GetMeshes()) {
|
for (auto& mesh : model->GetMeshes()) {
|
||||||
@@ -461,11 +522,89 @@ namespace Engine
|
|||||||
|
|
||||||
cmd.endRendering();
|
cmd.endRendering();
|
||||||
})
|
})
|
||||||
.Write(m_Backbuffer, RG::ResourceUse::ColorAttachment)
|
.Write(m_HDRColor, RG::ResourceUse::ColorAttachment)
|
||||||
.Write(m_Depth, RG::ResourceUse::DepthAttachment)
|
.Write(m_Depth, RG::ResourceUse::DepthAttachment)
|
||||||
.Read(m_ShadowMap, RG::ResourceUse::ShaderRead,
|
.Read(m_ShadowMap, RG::ResourceUse::ShaderRead,
|
||||||
vk::ShaderStageFlagBits::eFragment);
|
vk::ShaderStageFlagBits::eFragment);
|
||||||
|
|
||||||
|
// ---- Tonemap pass:HDR → 显示空间,全屏三角无顶点缓冲 ----
|
||||||
|
// 显示变换在此显式建模(此前隐式依赖 Srgb 附件,是设计欠账)。
|
||||||
|
// Backbuffer 从此只接收"显示空间"颜色:tonemap 直接写、ImGui 叠加。
|
||||||
|
m_RenderGraph->AddPass("Tonemap",
|
||||||
|
[this](RG::RenderGraphContext& ctx) {
|
||||||
|
const vk::CommandBuffer cmd = ctx.cmd;
|
||||||
|
|
||||||
|
vk::ClearValue clearValue;
|
||||||
|
clearValue.color = vk::ClearColorValue(
|
||||||
|
std::array<float, 4>{0.0f, 0.0f, 0.0f, 1.0f});
|
||||||
|
|
||||||
|
vk::RenderingAttachmentInfo colorAttachment;
|
||||||
|
colorAttachment.setImageView(ctx.View(m_Backbuffer))
|
||||||
|
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
|
||||||
|
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
||||||
|
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
||||||
|
.setClearValue(clearValue);
|
||||||
|
|
||||||
|
vk::RenderingInfo renderingInfo;
|
||||||
|
renderingInfo.setRenderArea({{0, 0}, ctx.extent})
|
||||||
|
.setLayerCount(1)
|
||||||
|
.setColorAttachments(colorAttachment);
|
||||||
|
|
||||||
|
cmd.beginRendering(renderingInfo);
|
||||||
|
|
||||||
|
std::array<vk::ShaderStageFlagBits, 2> stages = {
|
||||||
|
vk::ShaderStageFlagBits::eVertex,
|
||||||
|
vk::ShaderStageFlagBits::eFragment
|
||||||
|
};
|
||||||
|
std::array<vk::ShaderEXT, 2> shaders = {
|
||||||
|
m_TonemapVertShader, m_TonemapFragShader};
|
||||||
|
cmd.bindShadersEXT(stages, shaders);
|
||||||
|
|
||||||
|
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||||
|
m_TonemapPipelineLayout, 0,
|
||||||
|
m_TonemapDescriptorSets[ctx.frame.frameSlot], {});
|
||||||
|
|
||||||
|
// ---- 动态状态(无深度附件、无顶点输入)----
|
||||||
|
cmd.setRasterizerDiscardEnable(VK_FALSE);
|
||||||
|
cmd.setDepthTestEnable(VK_FALSE);
|
||||||
|
cmd.setDepthWriteEnable(VK_FALSE);
|
||||||
|
cmd.setDepthBoundsTestEnable(VK_FALSE);
|
||||||
|
cmd.setStencilTestEnable(VK_FALSE);
|
||||||
|
cmd.setDepthBiasEnable(VK_FALSE);
|
||||||
|
cmd.setPolygonModeEXT(vk::PolygonMode::eFill);
|
||||||
|
cmd.setCullMode(vk::CullModeFlagBits::eNone);
|
||||||
|
cmd.setFrontFace(vk::FrontFace::eCounterClockwise);
|
||||||
|
cmd.setPrimitiveTopology(vk::PrimitiveTopology::eTriangleList);
|
||||||
|
cmd.setPrimitiveRestartEnable(VK_FALSE);
|
||||||
|
cmd.setVertexInputEXT(
|
||||||
|
std::array<vk::VertexInputBindingDescription2EXT, 0>{},
|
||||||
|
std::array<vk::VertexInputAttributeDescription2EXT, 0>{});
|
||||||
|
cmd.setRasterizationSamplesEXT(vk::SampleCountFlagBits::e1);
|
||||||
|
vk::SampleMask sampleMask = 0xFFFFFFFF;
|
||||||
|
cmd.setSampleMaskEXT(vk::SampleCountFlagBits::e1, sampleMask);
|
||||||
|
cmd.setAlphaToCoverageEnableEXT(VK_FALSE);
|
||||||
|
cmd.setAlphaToOneEnableEXT(VK_FALSE);
|
||||||
|
|
||||||
|
vk::Viewport viewport(0.0f, 0.0f,
|
||||||
|
static_cast<float>(ctx.extent.width),
|
||||||
|
static_cast<float>(ctx.extent.height),
|
||||||
|
0.0f, 1.0f);
|
||||||
|
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);
|
||||||
|
|
||||||
|
cmd.draw(3, 1, 0, 0);
|
||||||
|
cmd.endRendering();
|
||||||
|
})
|
||||||
|
.Write(m_Backbuffer, RG::ResourceUse::ColorAttachment)
|
||||||
|
.Read(m_HDRColor, RG::ResourceUse::ShaderRead,
|
||||||
|
vk::ShaderStageFlagBits::eFragment);
|
||||||
|
|
||||||
// ---- ImGui pass:叠加层(loadOp=Load,不清屏)----
|
// ---- ImGui pass:叠加层(loadOp=Load,不清屏)----
|
||||||
// 框架只负责把 draw data 录进 cmd;UI 内容全部由派生类
|
// 框架只负责把 draw data 录进 cmd;UI 内容全部由派生类
|
||||||
// 在 OnRenderOverlay() 里构建,框架自身不画任何 ImGui 控件。
|
// 在 OnRenderOverlay() 里构建,框架自身不画任何 ImGui 控件。
|
||||||
@@ -520,8 +659,9 @@ namespace Engine
|
|||||||
// RG 重编译:物理镜像按新尺寸轮换
|
// RG 重编译:物理镜像按新尺寸轮换
|
||||||
m_RenderGraph->SetTargetExtent(m_Extent);
|
m_RenderGraph->SetTargetExtent(m_Extent);
|
||||||
|
|
||||||
// 重编译后物理镜像可能变化,阴影描述符重新绑定
|
// 重编译后物理镜像可能变化,描述符重新绑定
|
||||||
UpdateShadowDescriptors();
|
UpdateShadowDescriptors();
|
||||||
|
UpdateTonemapDescriptors();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::UpdateShadowDescriptors()
|
void Renderer::UpdateShadowDescriptors()
|
||||||
@@ -535,6 +675,16 @@ namespace Engine
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Renderer::UpdateTonemapDescriptors()
|
||||||
|
{
|
||||||
|
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||||
|
m_DescriptorManager->UpdateTexture(
|
||||||
|
m_TonemapDescriptorSets[slot], 0,
|
||||||
|
m_TonemapSampler,
|
||||||
|
m_RenderGraph->GetPhysicalView(m_HDRColor, slot));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Renderer::Render(ECS::Scene& scene, Assets::AssetManager& assets)
|
void Renderer::Render(ECS::Scene& scene, Assets::AssetManager& assets)
|
||||||
{
|
{
|
||||||
m_BoundScene = &scene;
|
m_BoundScene = &scene;
|
||||||
|
|||||||
@@ -115,14 +115,19 @@ namespace Engine
|
|||||||
return m_Device.GetDevice().createPipelineLayout(info);
|
return m_Device.GetDevice().createPipelineLayout(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::PipelineLayout DescriptorManager::CreatePipelineLayout(const std::vector<vk::DescriptorSetLayout>& layouts,u32 pushConstantSize)
|
vk::PipelineLayout DescriptorManager::CreatePipelineLayout(const std::vector<vk::DescriptorSetLayout>& layouts,
|
||||||
|
u32 pushConstantSize)
|
||||||
{
|
{
|
||||||
vk::PipelineLayoutCreateInfo info;
|
vk::PipelineLayoutCreateInfo info;
|
||||||
info.setSetLayouts(layouts);
|
info.setSetLayouts(layouts);
|
||||||
|
|
||||||
std::array<vk::PushConstantRange, 1> pushRanges;
|
std::array<vk::PushConstantRange, 1> pushRanges;
|
||||||
if (pushConstantSize > 0) {
|
if (pushConstantSize > 0) {
|
||||||
pushRanges[0].setStageFlags(vk::ShaderStageFlagBits::eVertex)
|
// 与 ShaderManager 一致:程序级 vertex|fragment 声明。
|
||||||
|
// 注意 vkCmdPushConstants 的 stageFlags 必须覆盖 range 的
|
||||||
|
// 全部 stage(VUID-01796),因此 push 调用点也要带双 stage
|
||||||
|
pushRanges[0].setStageFlags(vk::ShaderStageFlagBits::eVertex |
|
||||||
|
vk::ShaderStageFlagBits::eFragment)
|
||||||
.setOffset(0)
|
.setOffset(0)
|
||||||
.setSize(pushConstantSize);
|
.setSize(pushConstantSize);
|
||||||
info.setPushConstantRanges(pushRanges);
|
info.setPushConstantRanges(pushRanges);
|
||||||
|
|||||||
@@ -57,7 +57,12 @@ ShaderObject ShaderManager::CreateFromSPIRV(const std::vector<u32>& spirv,
|
|||||||
|
|
||||||
std::array<vk::PushConstantRange, 1> pushRanges;
|
std::array<vk::PushConstantRange, 1> pushRanges;
|
||||||
if (pushConstantSize > 0) {
|
if (pushConstantSize > 0) {
|
||||||
pushRanges[0].setStageFlags(vk::ShaderStageFlagBits::eVertex)
|
// push range 是"程序级"声明:shader object 要求同一程序绑定的
|
||||||
|
// 全部 shader(vert+frag)push range 定义完全一致(VUID-08878),
|
||||||
|
// 因此统一按 vertex|fragment 声明;shader 实际读取哪些字节由
|
||||||
|
// GLSL 的 push_constant block 决定,声明范围只是能力上限
|
||||||
|
pushRanges[0].setStageFlags(vk::ShaderStageFlagBits::eVertex |
|
||||||
|
vk::ShaderStageFlagBits::eFragment)
|
||||||
.setOffset(0)
|
.setOffset(0)
|
||||||
.setSize(pushConstantSize);
|
.setSize(pushConstantSize);
|
||||||
createInfo.setPushConstantRanges(pushRanges);
|
createInfo.setPushConstantRanges(pushRanges);
|
||||||
|
|||||||
@@ -134,9 +134,13 @@ namespace Engine
|
|||||||
|
|
||||||
vk::SurfaceFormatKHR Swapchain::ChooseSurfaceFormat(const std::vector<vk::SurfaceFormatKHR>& formats)
|
vk::SurfaceFormatKHR Swapchain::ChooseSurfaceFormat(const std::vector<vk::SurfaceFormatKHR>& formats)
|
||||||
{
|
{
|
||||||
|
// 用 Unorm 而非 Srgb:当前管线(场景光照 + ImGui)都是把
|
||||||
|
// "显示空间"颜色直接写进 framebuffer,若用 Srgb 附件硬件会再
|
||||||
|
// 做一次 linear→sRGB 提亮 → 整体泛白(ImGui 原生蓝 #4296FA
|
||||||
|
// 会被洗成 #8CCBFC)。等上 HDR/tonemap 管线后再切回 Srgb。
|
||||||
for (const auto& f : formats)
|
for (const auto& f : formats)
|
||||||
{
|
{
|
||||||
if (f.format == vk::Format::eB8G8R8A8Srgb &&
|
if (f.format == vk::Format::eB8G8R8A8Unorm &&
|
||||||
f.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear)
|
f.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear)
|
||||||
{
|
{
|
||||||
return f;
|
return f;
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
// Tonemap pass:HDR 线性颜色 → 显示空间。
|
||||||
|
// 这是显示变换的显式建模(对齐 UE/Unity:HDR SceneColor → tonemapper → UI)。
|
||||||
|
// 当前 = 曝光 + gamma 编码(与旧 Srgb 附件行为等价,观感不变);
|
||||||
|
// ACES/Reinhard 色调曲线、bloom 等后处理在此 pass 基础上扩展。
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 fragUV;
|
||||||
|
layout(location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
layout(set = 0, binding = 0) uniform sampler2D hdrImage;
|
||||||
|
|
||||||
|
layout(push_constant) uniform PushConstants {
|
||||||
|
float exposure; // 曝光系数(后续接 ImGui)
|
||||||
|
} pc;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 hdr = texture(hdrImage, fragUV).rgb * pc.exposure;
|
||||||
|
|
||||||
|
// 显示编码:linear → sRGB(gamma 2.2 近似)。
|
||||||
|
// TODO: ACES 拟合曲线接入点(HDR 泛光/色调映射时替换此行)
|
||||||
|
outColor = vec4(pow(clamp(hdr, 0.0, 1.0), vec3(1.0 / 2.2)), 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
// 全屏三角形:无顶点缓冲,从 gl_VertexIndex 派生 UV。
|
||||||
|
// 0 → (-1,3)? 经典三角覆盖整个屏幕:
|
||||||
|
// idx0:(-1,-1) idx1:(3,-1) idx2:(-1,3),UV 相应 [0..2],
|
||||||
|
// 光栅化插值后屏幕内 UV 恰好落在 [0,1]。
|
||||||
|
layout(location = 0) out vec2 fragUV;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 p = vec2(
|
||||||
|
float((gl_VertexIndex << 1) & 2),
|
||||||
|
float(gl_VertexIndex & 2));
|
||||||
|
fragUV = p;
|
||||||
|
gl_Position = vec4(p * 2.0 - 1.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user