refactor: 解耦重构——还清四处架构债(对齐 UE/Unity 分层)

- SceneRenderer:场景→渲染数据翻译层(UE FSceneRenderer / Unity CullingResults
  模式),收口相机/灯光/光空间矩阵/双视图剔除,Renderer 只编排
- pass 对象化(Unity ScriptableRenderPass 模式):8 个 pass lambda 拆为
  Passes::{Shadow,Forward,Bloom,Tonemap,ImGui,Present},BuildGraph 超级函数消失;
  SceneGpu/FullscreenGpu 承接共享装配
- 中性 AssetRef(Core 层):ECS 组件不再依赖 Assets 模块(Unity InstanceID 思路)
- IAssetUploader 接口:AssetManager 不感知 Vulkan 具体类,上传服务由渲染层
  VulkanAssetUploader 提供
- Renderer 公共 API 不变(App 层零改动),编译零错误 validation 零警告
This commit is contained in:
2026-09-23 12:02:49 +08:00
parent 91880d88fe
commit 718a658f5d
30 changed files with 1777 additions and 1054 deletions
+75
View File
@@ -0,0 +1,75 @@
//
// Created by atdunbg on 2026/9/23.
//
#include <Engine/Renderer/Passes/BloomPass.h>
#include <Engine/Core/FrameContext.h>
namespace Engine::Passes
{
BloomPass::BloomPass(FullscreenGpu& gpu, PostProcessParams& params,
RG::TextureHandle hdrColor,
RG::TextureHandle bloomA, RG::TextureHandle bloomB)
: m_Gpu(gpu), m_Params(params),
m_HDR(hdrColor), m_A(bloomA), m_B(bloomB)
{
m_ExtractSets.resize(kMaxFramesInFlight);
m_BlurHSets.resize(kMaxFramesInFlight);
m_BlurVSets.resize(kMaxFramesInFlight);
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
m_ExtractSets[slot] = m_Gpu.AllocateSet();
m_BlurHSets[slot] = m_Gpu.AllocateSet();
m_BlurVSets[slot] = m_Gpu.AllocateSet();
}
}
void BloomPass::Register(RG::RenderGraph& rg)
{
// Extract:HDR → 半分辨率 A(软阈值由 frag 的 param0 传入)
rg.AddPass("BloomExtract",
[this](RG::RenderGraphContext& ctx) {
m_Gpu.RecordFullscreen(ctx, ctx.View(m_A), m_Gpu.extractFrag,
m_ExtractSets[ctx.frame.frameSlot],
FullscreenPush{0.0f, m_Params.bloomThreshold, 0.0f, 0.0f});
})
.Read(m_HDR, RG::ResourceUse::ShaderRead,
vk::ShaderStageFlagBits::eFragment)
.Write(m_A, RG::ResourceUse::ColorAttachment);
// BlurH:A → B(水平步长经 push 传入)
rg.AddPass("BloomBlurH",
[this](RG::RenderGraphContext& ctx) {
m_Gpu.RecordFullscreen(ctx, ctx.View(m_B), m_Gpu.blurFrag,
m_BlurHSets[ctx.frame.frameSlot],
FullscreenPush{0.0f,
1.0f / ctx.extent.width, 0.0f,
0.0f});
})
.Read(m_A, RG::ResourceUse::ShaderRead,
vk::ShaderStageFlagBits::eFragment)
.Write(m_B, RG::ResourceUse::ColorAttachment);
// BlurV:B → A(垂直步长)
rg.AddPass("BloomBlurV",
[this](RG::RenderGraphContext& ctx) {
m_Gpu.RecordFullscreen(ctx, ctx.View(m_A), m_Gpu.blurFrag,
m_BlurVSets[ctx.frame.frameSlot],
FullscreenPush{0.0f, 0.0f,
1.0f / ctx.extent.height,
0.0f});
})
.Read(m_B, RG::ResourceUse::ShaderRead,
vk::ShaderStageFlagBits::eFragment)
.Write(m_A, RG::ResourceUse::ColorAttachment);
}
void BloomPass::OnTargetsRebuilt(RG::RenderGraph& rg)
{
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
m_Gpu.UpdateSetInput(m_ExtractSets[slot], 0, rg, m_HDR, slot);
m_Gpu.UpdateSetInput(m_BlurHSets[slot], 0, rg, m_A, slot);
m_Gpu.UpdateSetInput(m_BlurVSets[slot], 0, rg, m_B, slot);
}
}
} // namespace Engine::Passes
+135
View File
@@ -0,0 +1,135 @@
//
// Created by atdunbg on 2026/9/23.
//
#include <Engine/Renderer/Passes/ForwardPass.h>
#include <Engine/Renderer/Vertex.h>
#include <Engine/Renderer/Mesh.h>
#include <Engine/Renderer/Material.h>
#include <array>
namespace Engine::Passes
{
void ForwardPass::Register(RG::RenderGraph& rg)
{
rg.AddPass("Forward", [this](RG::RenderGraphContext& ctx) {
const vk::CommandBuffer cmd = ctx.cmd;
vk::ClearValue clearValue;
clearValue.color = vk::ClearColorValue(
std::array<float, 4>{0.1f, 0.1f, 0.1f, 0.0f});
vk::RenderingAttachmentInfo colorAttachment;
colorAttachment.setImageView(ctx.View(m_HDRColor))
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
.setLoadOp(vk::AttachmentLoadOp::eClear)
.setStoreOp(vk::AttachmentStoreOp::eStore)
.setClearValue(clearValue);
vk::RenderingAttachmentInfo depthAttachment;
depthAttachment.setImageView(ctx.View(m_Depth))
.setImageLayout(vk::ImageLayout::eDepthAttachmentOptimal)
.setLoadOp(vk::AttachmentLoadOp::eClear)
.setStoreOp(vk::AttachmentStoreOp::eDontCare)
.setClearValue(vk::ClearValue(
vk::ClearDepthStencilValue(1.0f, 0)));
vk::RenderingInfo renderingInfo;
renderingInfo.setRenderArea({{0, 0}, ctx.extent})
.setLayerCount(1)
.setColorAttachments(colorAttachment)
.setPDepthAttachment(&depthAttachment);
cmd.beginRendering(renderingInfo);
// 绑定 shader object
std::array<vk::ShaderStageFlagBits, 2> stages = {
vk::ShaderStageFlagBits::eVertex,
vk::ShaderStageFlagBits::eFragment
};
std::array<vk::ShaderEXT, 2> shaders = {m_Gpu.forwardVert, m_Gpu.forwardFrag};
cmd.bindShadersEXT(stages, shaders);
// Set 0:本帧槽位的 UBO;Set 2:本帧槽位的阴影图
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
m_Gpu.forwardLayout, 0,
m_Gpu.uboSets[ctx.frame.frameSlot], {});
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
m_Gpu.forwardLayout, 2,
m_Gpu.shadowSets[ctx.frame.frameSlot], {});
// ---- 动态状态 ----
cmd.setRasterizerDiscardEnable(VK_FALSE);
cmd.setDepthTestEnable(VK_TRUE);
cmd.setDepthWriteEnable(VK_TRUE);
cmd.setDepthCompareOp(vk::CompareOp::eLess);
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);
auto binding = Vertex::GetBindingDescription();
auto attributes = Vertex::GetAttributeDescriptions();
cmd.setVertexInputEXT(binding, attributes);
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);
vk::Rect2D scissor({0, 0}, ctx.extent);
cmd.setScissorWithCount(scissor);
vk::Bool32 blendEnable = VK_FALSE;
cmd.setColorBlendEnableEXT(0, blendEnable);
vk::ColorBlendEquationEXT blendEquation;
blendEquation.setSrcColorBlendFactor(vk::BlendFactor::eOne)
.setDstColorBlendFactor(vk::BlendFactor::eZero)
.setColorBlendOp(vk::BlendOp::eAdd)
.setSrcAlphaBlendFactor(vk::BlendFactor::eOne)
.setDstAlphaBlendFactor(vk::BlendFactor::eZero)
.setAlphaBlendOp(vk::BlendOp::eAdd);
cmd.setColorBlendEquationEXT(0, blendEquation);
vk::ColorComponentFlags colorWriteMask =
vk::ColorComponentFlagBits::eR |
vk::ColorComponentFlagBits::eG |
vk::ColorComponentFlagBits::eB |
vk::ColorComponentFlagBits::eA;
cmd.setColorWriteMaskEXT(0, colorWriteMask);
// ---- 绘制:GPUDriven 按 mesh 分 batch(相机视锥剔除后)----
// 实例矩阵已由 Renderer 写入 ObjectBuffer(per-slot SSBO);
// 同 mesh 的 N 个可见实体合并为一次 drawIndexed(instanceCount=N),
// draw 数 = 可见唯一 mesh 数,与实体数量解耦
for (const auto& batch : m_Frame.mainView.batches) {
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
m_Gpu.forwardLayout, 1,
batch.mesh->GetMaterial()->GetDescriptorSet(), {});
batch.mesh->Draw(cmd, batch.count, batch.first);
}
cmd.endRendering();
})
.Write(m_HDRColor, RG::ResourceUse::ColorAttachment)
.Write(m_Depth, RG::ResourceUse::DepthAttachment)
.Read(m_ShadowMap, RG::ResourceUse::ShaderRead,
vk::ShaderStageFlagBits::eFragment);
}
} // namespace Engine::Passes
@@ -0,0 +1,174 @@
//
// Created by atdunbg on 2026/9/23.
//
#include <Engine/Renderer/Passes/FullscreenGpu.h>
#include <Engine/Vulkan/Device.h>
#include <Engine/Vulkan/DescriptorManager.h>
#include <Engine/Vulkan/ShaderManager.h>
#include <Engine/Core/Log.h>
#include <array>
namespace Engine::Passes
{
FullscreenGpu::FullscreenGpu(Device& device, DescriptorManager& descriptors,
ShaderManager& shaders)
: m_Device(device), m_Descriptors(descriptors)
{
// Set0 = {HDR 颜色图, Bloom 图},push = 16B 通用结构
std::vector<DescriptorBinding> bindings = {
{0, vk::DescriptorType::eCombinedImageSampler,
vk::ShaderStageFlagBits::eFragment},
{1, vk::DescriptorType::eCombinedImageSampler,
vk::ShaderStageFlagBits::eFragment},
};
setLayout = descriptors.CreateLayout(bindings);
layout = descriptors.CreatePipelineLayout(
std::vector<vk::DescriptorSetLayout>{setLayout},
static_cast<u32>(sizeof(FullscreenPush)));
sampler = 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));
const std::string shaderDir = "shaders/";
auto vertShader = shaders.CreateFromFile(
shaderDir + "tonemap.vert.spv",
vk::ShaderStageFlagBits::eVertex,
std::vector<vk::DescriptorSetLayout>{setLayout},
static_cast<u32>(sizeof(FullscreenPush)));
auto tonemapF = shaders.CreateFromFile(
shaderDir + "tonemap.frag.spv",
vk::ShaderStageFlagBits::eFragment,
std::vector<vk::DescriptorSetLayout>{setLayout},
static_cast<u32>(sizeof(FullscreenPush)));
auto extractF = shaders.CreateFromFile(
shaderDir + "bloom_extract.frag.spv",
vk::ShaderStageFlagBits::eFragment,
std::vector<vk::DescriptorSetLayout>{setLayout},
static_cast<u32>(sizeof(FullscreenPush)));
auto blurF = shaders.CreateFromFile(
shaderDir + "bloom_blur.frag.spv",
vk::ShaderStageFlagBits::eFragment,
std::vector<vk::DescriptorSetLayout>{setLayout},
static_cast<u32>(sizeof(FullscreenPush)));
vert = vertShader.Handle;
tonemapFrag = tonemapF.Handle;
extractFrag = extractF.Handle;
blurFrag = blurF.Handle;
if (!vert || !tonemapFrag || !extractFrag || !blurFrag) {
ENGINE_FATAL("FullscreenGpu: failed to create shader objects");
}
}
FullscreenGpu::~FullscreenGpu()
{
const auto& vkDevice = m_Device.GetDevice();
if (vert) vkDevice.destroyShaderEXT(vert);
if (tonemapFrag) vkDevice.destroyShaderEXT(tonemapFrag);
if (extractFrag) vkDevice.destroyShaderEXT(extractFrag);
if (blurFrag) vkDevice.destroyShaderEXT(blurFrag);
vkDevice.destroySampler(sampler);
vkDevice.destroyPipelineLayout(layout);
vkDevice.destroyDescriptorSetLayout(setLayout);
}
vk::DescriptorSet FullscreenGpu::AllocateSet()
{
return m_Descriptors.AllocateDescriptorSet(setLayout);
}
void FullscreenGpu::UpdateSetInput(vk::DescriptorSet set, u32 binding,
RG::RenderGraph& rg, RG::TextureHandle input,
u32 slot)
{
// 物理镜像按帧槽位各一份:每个槽位的描述符绑各自的 view
m_Descriptors.UpdateTexture(set, binding, sampler,
rg.GetPhysicalView(input, slot));
}
void FullscreenGpu::RecordFullscreen(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;
clearValue.color = vk::ClearColorValue(
std::array<float, 4>{0.0f, 0.0f, 0.0f, 1.0f});
vk::RenderingAttachmentInfo colorAttachment;
colorAttachment.setImageView(view)
.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 = {vert, frag};
cmd.bindShadersEXT(stages, shaders);
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
layout, 0, set, {});
// ---- 动态状态(无深度附件、无顶点输入)----
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));
cmd.pushConstants(layout,
vk::ShaderStageFlagBits::eVertex |
vk::ShaderStageFlagBits::eFragment,
0, sizeof(FullscreenPush), &push);
cmd.draw(3, 1, 0, 0);
cmd.endRendering();
}
} // namespace Engine::Passes
+36
View File
@@ -0,0 +1,36 @@
//
// Created by atdunbg on 2026/9/23.
//
#include <Engine/Renderer/Passes/ImGuiPass.h>
#include <Engine/ImGui/ImGuiLayer.h>
namespace Engine::Passes
{
void ImGuiPass::Register(RG::RenderGraph& rg)
{
rg.AddPass("ImGui", [this](RG::RenderGraphContext& ctx) {
const vk::CommandBuffer cmd = ctx.cmd;
vk::RenderingAttachmentInfo colorAttachment;
colorAttachment.setImageView(ctx.View(m_Backbuffer))
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
.setLoadOp(vk::AttachmentLoadOp::eLoad) // 关键:叠加
.setStoreOp(vk::AttachmentStoreOp::eStore);
vk::RenderingInfo renderingInfo;
renderingInfo.setRenderArea({{0, 0}, ctx.extent})
.setLayerCount(1)
.setColorAttachments(colorAttachment);
cmd.beginRendering(renderingInfo);
if (*m_Layer) {
(*m_Layer)->RenderDrawData(cmd);
}
cmd.endRendering();
}).Write(m_Backbuffer, RG::ResourceUse::ColorAttachment);
}
} // namespace Engine::Passes
+139
View File
@@ -0,0 +1,139 @@
//
// Created by atdunbg on 2026/9/23.
//
#include <Engine/Renderer/Passes/SceneGpu.h>
#include <Engine/Vulkan/Device.h>
#include <Engine/Vulkan/DescriptorManager.h>
#include <Engine/Vulkan/ShaderManager.h>
#include <Engine/Vulkan/UniformBuffer.h>
#include <Engine/Core/Log.h>
#include <glm/glm.hpp>
namespace Engine::Passes
{
SceneGpu::SceneGpu(Device& device, DescriptorManager& descriptors,
ShaderManager& shaders,
UniformBuffer& ubo, UniformBuffer& objectBuffer)
: m_Device(device), m_Descriptors(descriptors)
{
// Set 0: UBO + GPUDriven 实例数据(SSBO,vertex 读取)
std::vector<DescriptorBinding> uboBindings = {
{0, vk::DescriptorType::eUniformBuffer,
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
{1, vk::DescriptorType::eStorageBuffer,
vk::ShaderStageFlagBits::eVertex},
};
uboSetLayout = descriptors.CreateLayout(uboBindings);
// Set 1: texture
std::vector<DescriptorBinding> texBindings = {
{0, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment},
};
textureSetLayout = descriptors.CreateLayout(texBindings);
// Set 2: shadow map(fragment 采样)
std::vector<DescriptorBinding> shadowBindings = {
{0, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment},
};
shadowSetLayout = descriptors.CreateLayout(shadowBindings);
// Forward pipeline layout:三个 set;无 push(GPUDriven 经 SSBO)
forwardLayout = descriptors.CreatePipelineLayout(
{uboSetLayout, textureSetLayout, shadowSetLayout}, 0);
// Shadow pipeline layout:Set0 = UBO/ObjectBuffer(实例矩阵),
// push = lightVP 64B
shadowLayout = descriptors.CreatePipelineLayout(
std::vector<vk::DescriptorSetLayout>{uboSetLayout},
sizeof(glm::mat4));
const std::string shaderDir = "shaders/";
// forward vert/frag 均无 push(range 定义一致:都没有,VUID-08878)
auto vert = shaders.CreateFromFile(
shaderDir + "simple_shader.vert.spv",
vk::ShaderStageFlagBits::eVertex,
{uboSetLayout, textureSetLayout, shadowSetLayout}, 0);
auto frag = shaders.CreateFromFile(
shaderDir + "simple_shader.frag.spv",
vk::ShaderStageFlagBits::eFragment,
{uboSetLayout, textureSetLayout, shadowSetLayout}, 0);
// shadow shader:depth-only,push = lightVP
auto shadowVertShader = shaders.CreateFromFile(
shaderDir + "shadow.vert.spv",
vk::ShaderStageFlagBits::eVertex,
std::vector<vk::DescriptorSetLayout>{uboSetLayout},
sizeof(glm::mat4));
auto shadowFragShader = shaders.CreateFromFile(
shaderDir + "shadow.frag.spv",
vk::ShaderStageFlagBits::eFragment,
std::vector<vk::DescriptorSetLayout>{uboSetLayout},
sizeof(glm::mat4));
forwardVert = vert.Handle;
forwardFrag = frag.Handle;
shadowVert = shadowVertShader.Handle;
shadowFrag = shadowFragShader.Handle;
if (!forwardVert || !forwardFrag || !shadowVert || !shadowFrag) {
ENGINE_FATAL("SceneGpu: failed to create shader objects");
}
// 阴影采样器:clamp-to-border 白色 = 光视锥之外无阴影
vk::SamplerCreateInfo shadowSamplerInfo;
shadowSamplerInfo.setMagFilter(vk::Filter::eLinear)
.setMinFilter(vk::Filter::eLinear)
.setMipmapMode(vk::SamplerMipmapMode::eNearest)
.setAddressModeU(vk::SamplerAddressMode::eClampToBorder)
.setAddressModeV(vk::SamplerAddressMode::eClampToBorder)
.setAddressModeW(vk::SamplerAddressMode::eClampToBorder)
.setBorderColor(vk::BorderColor::eFloatOpaqueWhite)
.setUnnormalizedCoordinates(VK_FALSE)
.setCompareEnable(VK_FALSE);
shadowSampler = device.GetDevice().createSampler(shadowSamplerInfo);
// per-slot 描述符分配 + Set0 绑定(buffer 句柄全程稳定,不用重绑)
uboSets.resize(kMaxFramesInFlight);
shadowSets.resize(kMaxFramesInFlight);
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
uboSets[slot] = descriptors.AllocateDescriptorSet(uboSetLayout);
descriptors.UpdateUniformBuffer(
uboSets[slot], 0, ubo.Get(slot), ubo.GetSize());
descriptors.UpdateStorageBuffer(
uboSets[slot], 1, objectBuffer.Get(slot), objectBuffer.GetSize());
shadowSets[slot] = descriptors.AllocateDescriptorSet(shadowSetLayout);
}
// shadowSets 的图片绑定在 Compile 后由 BindShadowMap 完成
}
SceneGpu::~SceneGpu()
{
const auto& vkDevice = m_Device.GetDevice();
if (forwardVert) vkDevice.destroyShaderEXT(forwardVert);
if (forwardFrag) vkDevice.destroyShaderEXT(forwardFrag);
if (shadowVert) vkDevice.destroyShaderEXT(shadowVert);
if (shadowFrag) vkDevice.destroyShaderEXT(shadowFrag);
vkDevice.destroySampler(shadowSampler);
vkDevice.destroyPipelineLayout(forwardLayout);
vkDevice.destroyPipelineLayout(shadowLayout);
vkDevice.destroyDescriptorSetLayout(uboSetLayout);
vkDevice.destroyDescriptorSetLayout(textureSetLayout);
vkDevice.destroyDescriptorSetLayout(shadowSetLayout);
}
void SceneGpu::BindShadowMap(RG::RenderGraph& rg, RG::TextureHandle shadowMap)
{
// 物理镜像按帧槽位各一份:每个槽位的描述符绑各自的 view
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
m_Descriptors.UpdateTexture(
shadowSets[slot], 0, shadowSampler,
rg.GetPhysicalView(shadowMap, slot));
}
}
} // namespace Engine::Passes
+104
View File
@@ -0,0 +1,104 @@
//
// Created by atdunbg on 2026/9/23.
//
#include <Engine/Renderer/Passes/ShadowPass.h>
#include <Engine/Renderer/Vertex.h>
#include <Engine/Renderer/Mesh.h>
#include <array>
namespace Engine::Passes
{
void ShadowPass::Register(RG::RenderGraph& rg)
{
// 所有渲染体都写入,不做实体级开关——castShadow 应由材质/实体
// 数据驱动剔除,而非 pass 逻辑特判
rg.AddPass("Shadow", [this](RG::RenderGraphContext& ctx) {
const vk::CommandBuffer cmd = ctx.cmd;
vk::RenderingAttachmentInfo depthAttachment;
depthAttachment.setImageView(ctx.View(m_ShadowMap))
.setImageLayout(vk::ImageLayout::eDepthAttachmentOptimal)
.setLoadOp(vk::AttachmentLoadOp::eClear)
.setStoreOp(vk::AttachmentStoreOp::eStore)
.setClearValue(vk::ClearValue(
vk::ClearDepthStencilValue(1.0f, 0)));
vk::RenderingInfo renderingInfo;
renderingInfo.setRenderArea({{0, 0}, {kShadowMapSize, kShadowMapSize}})
.setLayerCount(1)
.setPDepthAttachment(&depthAttachment);
cmd.beginRendering(renderingInfo);
std::array<vk::ShaderStageFlagBits, 2> stages = {
vk::ShaderStageFlagBits::eVertex,
vk::ShaderStageFlagBits::eFragment
};
std::array<vk::ShaderEXT, 2> shaders = {m_Gpu.shadowVert, m_Gpu.shadowFrag};
cmd.bindShadersEXT(stages, shaders);
// ---- 动态状态(depth-only)----
cmd.setRasterizerDiscardEnable(VK_FALSE);
cmd.setDepthTestEnable(VK_TRUE);
cmd.setDepthWriteEnable(VK_TRUE);
cmd.setDepthCompareOp(vk::CompareOp::eLess);
cmd.setDepthBoundsTestEnable(VK_FALSE);
cmd.setStencilTestEnable(VK_FALSE);
// 深度 bias 压平 shadow acne(NDC 量级)
cmd.setDepthBiasEnable(VK_TRUE);
cmd.setDepthBias(0.0025f, 0.0f, 0.003f);
cmd.setPolygonModeEXT(vk::PolygonMode::eFill);
cmd.setCullMode(vk::CullModeFlagBits::eNone);
cmd.setFrontFace(vk::FrontFace::eCounterClockwise);
cmd.setPrimitiveTopology(vk::PrimitiveTopology::eTriangleList);
cmd.setPrimitiveRestartEnable(VK_FALSE);
auto binding = Vertex::GetBindingDescription();
auto attributes = Vertex::GetAttributeDescriptions();
cmd.setVertexInputEXT(binding, attributes);
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>(kShadowMapSize),
static_cast<float>(kShadowMapSize),
0.0f, 1.0f);
cmd.setViewportWithCount(viewport);
vk::Rect2D scissor({0, 0}, {kShadowMapSize, kShadowMapSize});
cmd.setScissorWithCount(scissor);
// ---- 绘制:GPUDriven——与 Forward 共用实例数据,但用
// 光源视锥剔除后的 batch(相机外实体仍投影子)----
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
m_Gpu.shadowLayout, 0,
m_Gpu.uboSets[ctx.frame.frameSlot], {});
// stage 必须覆盖 layout range 声明的全部 stage(VUID-01796)
cmd.pushConstants(m_Gpu.shadowLayout,
vk::ShaderStageFlagBits::eVertex |
vk::ShaderStageFlagBits::eFragment,
0, sizeof(glm::mat4), &m_Frame.ubo.lightSpace);
for (const auto& batch : m_Frame.shadowView.batches) {
batch.mesh->Draw(cmd, batch.count, batch.first);
}
cmd.endRendering();
}).Write(m_ShadowMap, RG::ResourceUse::DepthAttachment);
}
void ShadowPass::OnTargetsRebuilt(RG::RenderGraph& rg)
{
m_Gpu.BindShadowMap(rg, m_ShadowMap);
}
} // namespace Engine::Passes
@@ -0,0 +1,48 @@
//
// Created by atdunbg on 2026/9/23.
//
#include <Engine/Renderer/Passes/TonemapPass.h>
#include <Engine/Core/FrameContext.h>
namespace Engine::Passes
{
TonemapPass::TonemapPass(FullscreenGpu& gpu, PostProcessParams& params,
RG::TextureHandle hdrColor, RG::TextureHandle bloomA,
RG::TextureHandle backbuffer)
: m_Gpu(gpu), m_Params(params),
m_HDR(hdrColor), m_A(bloomA), m_Backbuffer(backbuffer)
{
m_Sets.resize(kMaxFramesInFlight);
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
m_Sets[slot] = m_Gpu.AllocateSet();
}
}
void TonemapPass::Register(RG::RenderGraph& rg)
{
rg.AddPass("Tonemap",
[this](RG::RenderGraphContext& ctx) {
m_Gpu.RecordFullscreen(ctx, ctx.View(m_Backbuffer),
m_Gpu.tonemapFrag,
m_Sets[ctx.frame.frameSlot],
FullscreenPush{m_Params.exposure,
m_Params.useACES ? 1.0f : 0.0f,
m_Params.bloomStrength, 0.0f});
})
.Write(m_Backbuffer, RG::ResourceUse::ColorAttachment)
.Read(m_HDR, RG::ResourceUse::ShaderRead,
vk::ShaderStageFlagBits::eFragment)
.Read(m_A, RG::ResourceUse::ShaderRead,
vk::ShaderStageFlagBits::eFragment);
}
void TonemapPass::OnTargetsRebuilt(RG::RenderGraph& rg)
{
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
m_Gpu.UpdateSetInput(m_Sets[slot], 0, rg, m_HDR, slot);
m_Gpu.UpdateSetInput(m_Sets[slot], 1, rg, m_A, slot);
}
}
} // namespace Engine::Passes
File diff suppressed because it is too large Load Diff
+168
View File
@@ -0,0 +1,168 @@
//
// Created by atdunbg on 2026/9/23.
//
#include <Engine/Renderer/SceneRenderer.h>
#include <Engine/ECS/Scene.h>
#include <Engine/ECS/Components.h>
#include <Engine/Renderer/Camera.h>
#include <Engine/Renderer/Frustum.h>
#include <Engine/Renderer/Model.h>
#include <Engine/Renderer/Mesh.h>
#include <Engine/Assets/AssetManager.h>
#include <Engine/Core/Log.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <chrono>
namespace Engine
{
using namespace ECS; // TransformComponent / Entity 等直接使用
namespace
{
// 光源正交包围盒(覆盖当前场景;后续按场景 AABB 自适应)
constexpr f32 kLightOrthoExtent = 15.0f;
constexpr f32 kLightDistance = 30.0f;
constexpr f32 kLightNear = 1.0f;
constexpr f32 kLightFar = 80.0f;
}
void SceneRenderer::ComputeLightSpace(const glm::vec3& shadowDir)
{
// 光源位置在光方向的反方向远处,看向场景中心
const glm::vec3 lightPos = -shadowDir * kLightDistance;
// up 与视线平行时叉积退化(NaN),切换 up
const glm::vec3 up = glm::abs(shadowDir.y) > 0.999f
? glm::vec3(0.0f, 0.0f, 1.0f) : glm::vec3(0.0f, 1.0f, 0.0f);
const glm::mat4 lightView = glm::lookAt(
lightPos, glm::vec3(0.0f), up);
glm::mat4 lightProj = glm::ortho(
-kLightOrthoExtent, kLightOrthoExtent,
-kLightOrthoExtent, kLightOrthoExtent,
kLightNear, kLightFar);
// Vulkan clip 深度 [0,1]:z' = 0.5*z + 0.5*w。
// ortho 的 w_clip = 1,偏移折算进 z 的平移分量(与 perspective 公式不同!)
lightProj[2][2] *= 0.5f;
lightProj[3][2] = lightProj[3][2] * 0.5f + 0.5f;
m_Frame.ubo.lightSpace = lightProj * lightView;
}
bool SceneRenderer::BuildFrame(ECS::Scene& scene, Assets::AssetManager& assets,
f32 aspect)
{
const auto collectStart = std::chrono::steady_clock::now();
// ---- 1. 相机 ----
auto* cameraComp = scene.GetPrimaryCamera();
if (!cameraComp) {
// 无相机:本帧跳过(正常场景必有相机,安全网)
return false;
}
const Camera& camera = cameraComp->camera;
UniformBufferObject& ubo = m_Frame.ubo;
ubo.view = camera.GetView();
ubo.proj = camera.GetProjection(aspect, 0.1f, 100.0f);
ubo.ambientColor = scene.GetSettings().ambientColor;
// ---- 2. 灯光(第一个灯作为阴影光源)----
u32 lightCount = 0;
glm::vec3 shadowDir{0.0f, -1.0f, 0.0f}; // 无灯兜底:头顶正射
scene.Each<LightComponent>([&](Entity, LightComponent& light) {
if (lightCount >= kMaxLights) return;
glm::vec3 dir = glm::length(light.direction) > 0.0f
? glm::normalize(light.direction)
: glm::vec3(0.0f);
ubo.lights[lightCount].direction = glm::vec4(dir, 0.0f);
ubo.lights[lightCount].color = light.color;
if (lightCount == 0) shadowDir = dir;
++lightCount;
});
for (u32 i = lightCount; i < kMaxLights; ++i) {
ubo.lights[i].direction = glm::vec4(0.0f);
ubo.lights[i].color = glm::vec4(0.0f);
}
ComputeLightSpace(shadowDir);
// ---- 3. GPUDriven:单趟收集 + 双视图剔除 ----
// 每个实例对相机视锥、光源视锥分别做包围球测试:
// 进相机视锥 → 主视图 batch(Forward 画)
// 进光源视锥 → 阴影 batch(Shadow 画)——相机外实体仍可投影子
// 实例矩阵数组 = [主视图段 | 阴影视图段],batch.first 为全局下标。
// 哈希聚合保证各视图内 batch 区间连续。
m_Frame.instanceModels.clear();
m_Frame.mainView.batches.clear();
m_Frame.shadowView.batches.clear();
m_Frame.mainView.instanceCount = 0;
m_Frame.shadowView.instanceCount = 0;
m_MainLookup.clear();
m_ShadowLookup.clear();
bool instanceTruncated = false;
// 剔除平面提取按 [0,1] 深度约定——相机 proj 与 lightSpace 都已
// 做 [0,1] 重映射;原始 [-1,1] 矩阵的 near 平面公式不同,勿用
const Frustum mainFrustum = Frustum::FromMatrix(ubo.proj * ubo.view);
const Frustum shadowFrustum = Frustum::FromMatrix(ubo.lightSpace);
scene.Each<TransformComponent, MeshRendererComponent>(
[&](Entity, TransformComponent& t, MeshRendererComponent& mr) {
Model* model = assets.GetModel(mr.model);
if (!model) return;
// 实例世界缩放(矩阵列向量最大长度,包围球半径随缩放)
const f32 worldScale = glm::max(
glm::length(glm::vec3(t.world[0])),
glm::max(glm::length(glm::vec3(t.world[1])),
glm::length(glm::vec3(t.world[2]))));
for (auto& mesh : model->GetMeshes()) {
const auto& bounds = mesh->GetBounds();
const glm::vec3 worldCenter =
glm::vec3(t.world * glm::vec4(bounds.center, 1.0f));
const f32 worldRadius = bounds.radius * worldScale;
auto tryAppend = [&](CullResult& view, const Frustum& frustum,
std::unordered_map<Mesh*, u32>& lookup) {
if (!frustum.IntersectsSphere(worldCenter, worldRadius)) {
return;
}
if (m_Frame.instanceModels.size() >= kMaxInstances) {
instanceTruncated = true;
return;
}
const auto [it, inserted] = lookup.emplace(
mesh.get(), static_cast<u32>(view.batches.size()));
if (inserted) {
view.batches.push_back({mesh.get(),
static_cast<u32>(m_Frame.instanceModels.size()), 0});
}
view.batches[it->second].count++;
view.instanceCount++;
m_Frame.instanceModels.push_back(t.world);
};
tryAppend(m_Frame.mainView, mainFrustum, m_MainLookup);
tryAppend(m_Frame.shadowView, shadowFrustum, m_ShadowLookup);
}
});
if (instanceTruncated && !m_TruncationWarned) {
// 只告警一次(每帧告警会刷屏)
m_TruncationWarned = true;
ENGINE_WARN("GPUDriven: instance buffer full ({}), entities truncated",
kMaxInstances);
}
m_Frame.collectMs = std::chrono::duration<f32, std::milli>(
std::chrono::steady_clock::now() - collectStart).count();
return true;
}
} // namespace Engine
@@ -0,0 +1,23 @@
//
// Created by atdunbg on 2026/9/23.
//
#include <Engine/Renderer/VulkanAssetUploader.h>
#include <Engine/Renderer/Model.h>
namespace Engine {
Scope<Model> VulkanAssetUploader::LoadModel(const std::string& path)
{
// GPU 上传在此发生:解析(assimp)+ buffer/image 创建一步完成
return CreateScope<Model>(
m_Device, m_Allocator, m_Descriptors, m_TextureLayout, path);
}
Scope<Model> VulkanAssetUploader::CreatePlane(f32 halfSize, u8 r, u8 g, u8 b)
{
return Model::CreatePlane(m_Device, m_Allocator, m_Descriptors,
m_TextureLayout, halfSize, r, g, b);
}
} // namespace Engine