feat: CSM 级联阴影(3 级联 × 1024²,实用分割 + 纹素对齐)

- SceneRenderer::ComputeCascades:相机视锥实用分割(λ=0.6)→ 反投影
  取切片角点 → 光空间 AABB 拟合 → 纹素对齐(消除相机移动时阴影边缘
  闪烁)→ [0,1] 深度重映射
- Shadow pass 单 pass 渲 3 个 region,每级联 push 各自光空间 VP
- Forward:UBO lightSpace[3]+cascadeSplits(视图深度选级联),Set2
  三张级联图按 binding 0/1/2 采样;vertex 输出 fragViewDepth
- 光源视锥剔除用最远级联(覆盖全阴影范围);双视锥独立剔除铁律保留
- 旧单张 2048 全场景图退役;近处级联分辨率 ~128px/unit(约 2× 提升)
- 编译零错误、validation 零警告、8 秒冒烟通过(10 pass 全活跃)
This commit is contained in:
2026-09-23 13:48:43 +08:00
parent 27b7f8785c
commit 0328d4aaeb
13 changed files with 250 additions and 141 deletions
+5 -1
View File
@@ -146,7 +146,11 @@ namespace Engine::Passes
.Write(m_HDRColor, RG::ResourceUse::ColorAttachment)
.Write(m_Velocity, RG::ResourceUse::ColorAttachment)
.Write(m_Depth, RG::ResourceUse::DepthAttachment)
.Read(m_ShadowMap, RG::ResourceUse::ShaderRead,
.Read(m_ShadowMaps[0], RG::ResourceUse::ShaderRead,
vk::ShaderStageFlagBits::eFragment)
.Read(m_ShadowMaps[1], RG::ResourceUse::ShaderRead,
vk::ShaderStageFlagBits::eFragment)
.Read(m_ShadowMaps[2], RG::ResourceUse::ShaderRead,
vk::ShaderStageFlagBits::eFragment);
}
+14 -8
View File
@@ -37,10 +37,13 @@ namespace Engine::Passes
};
textureSetLayout = descriptors.CreateLayout(texBindings);
// Set 2: shadow map(fragment 采样)
std::vector<DescriptorBinding> shadowBindings = {
{0, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment},
};
// Set 2: CSM 级联阴影图(fragment 采样,binding = 级联序号)
std::vector<DescriptorBinding> shadowBindings;
for (u32 c = 0; c < kMaxCascades; ++c) {
shadowBindings.push_back(
{c, vk::DescriptorType::eCombinedImageSampler,
vk::ShaderStageFlagBits::eFragment});
}
shadowSetLayout = descriptors.CreateLayout(shadowBindings);
// Forward pipeline layout:三个 set;无 push(GPUDriven 经 SSBO)
@@ -133,13 +136,16 @@ namespace Engine::Passes
vkDevice.destroyDescriptorSetLayout(shadowSetLayout);
}
void SceneGpu::BindShadowMap(RG::RenderGraph& rg, RG::TextureHandle shadowMap)
void SceneGpu::BindShadowMaps(RG::RenderGraph& rg,
const std::array<RG::TextureHandle, kMaxCascades>& maps)
{
// 物理镜像按帧槽位各一份:每个槽位的描述符绑各自的 view
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
m_Descriptors.UpdateTexture(
shadowSets[slot], 0, shadowSampler,
rg.GetPhysicalView(shadowMap, slot));
for (u32 c = 0; c < kMaxCascades; ++c) {
m_Descriptors.UpdateTexture(
shadowSets[slot], c, shadowSampler,
rg.GetPhysicalView(maps[c], slot));
}
}
}
+74 -66
View File
@@ -12,93 +12,101 @@ namespace Engine::Passes
{
void ShadowPass::Register(RG::RenderGraph& rg)
{
// 所有渲染体都写入,不做实体级开关——castShadow 应由材质/实体
// 数据驱动剔除,而非 pass 逻辑特判
// 所有渲染体都写入每个级联,不做实体级开关——castShadow 应由
// 材质/实体数据驱动剔除,而非 pass 逻辑特判。
// (每级联独立剔除是后续优化点:shadowView 细分为 per-cascade 列表)
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)));
for (u32 c = 0; c < kMaxCascades; ++c) {
vk::RenderingAttachmentInfo depthAttachment;
depthAttachment.setImageView(ctx.View(m_ShadowMaps[c]))
.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);
vk::RenderingInfo renderingInfo;
renderingInfo.setRenderArea({{0, 0}, {kCascadeRes, kCascadeRes}})
.setLayerCount(1)
.setPDepthAttachment(&depthAttachment);
cmd.beginRendering(renderingInfo);
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);
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);
// ---- 动态状态(depth-only)----
cmd.setRasterizerDiscardEnable(VK_FALSE);
cmd.setDepthTestEnable(VK_TRUE);
cmd.setDepthWriteEnable(VK_TRUE);
cmd.setDepthCompareOp(vk::CompareOp::eLess);
cmd.setDepthBoundsTestEnable(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);
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);
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);
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::Viewport viewport(0.0f, 0.0f,
static_cast<float>(kCascadeRes),
static_cast<float>(kCascadeRes),
0.0f, 1.0f);
cmd.setViewportWithCount(viewport);
vk::Rect2D scissor({0, 0}, {kShadowMapSize, kShadowMapSize});
cmd.setScissorWithCount(scissor);
vk::Rect2D scissor({0, 0}, {kCascadeRes, kCascadeRes});
cmd.setScissorWithCount(scissor);
// ---- 绘制:GPUDriven——与 Forward 共用实例数据,但用
// 光源视锥剔除后的 batch(相机外实体仍投影子)----
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
m_Gpu.shadowLayout, 0,
m_Gpu.uboSets[ctx.frame.frameSlot], {});
// ---- 绘制: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);
// stage 必须覆盖 layout range 声明的全部 stage(VUID-01796)
// 每级联 push 各自的光空间 VP
cmd.pushConstants(m_Gpu.shadowLayout,
vk::ShaderStageFlagBits::eVertex |
vk::ShaderStageFlagBits::eFragment,
0, sizeof(glm::mat4),
&m_Frame.ubo.lightSpace[c]);
for (const auto& batch : m_Frame.shadowView.batches) {
batch.mesh->Draw(cmd, batch.count, batch.first);
for (const auto& batch : m_Frame.shadowView.batches) {
batch.mesh->Draw(cmd, batch.count, batch.first);
}
cmd.endRendering();
}
cmd.endRendering();
}).Write(m_ShadowMap, RG::ResourceUse::DepthAttachment);
})
.Write(m_ShadowMaps[0], RG::ResourceUse::DepthAttachment)
.Write(m_ShadowMaps[1], RG::ResourceUse::DepthAttachment)
.Write(m_ShadowMaps[2], RG::ResourceUse::DepthAttachment);
}
void ShadowPass::OnTargetsRebuilt(RG::RenderGraph& rg)
{
m_Gpu.BindShadowMap(rg, m_ShadowMap);
m_Gpu.BindShadowMaps(rg, m_ShadowMaps);
}
} // namespace Engine::Passes