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
@@ -19,10 +19,11 @@ namespace Engine::Passes {
public:
ForwardPass(SceneGpu& gpu, FrameSceneData& frame,
RG::TextureHandle hdrColor, RG::TextureHandle velocity,
RG::TextureHandle depth, RG::TextureHandle shadowMap)
RG::TextureHandle depth,
const std::array<RG::TextureHandle, kMaxCascades>& shadowMaps)
: m_Gpu(gpu), m_Frame(frame),
m_HDRColor(hdrColor), m_Velocity(velocity),
m_Depth(depth), m_ShadowMap(shadowMap) {}
m_Depth(depth), m_ShadowMaps(shadowMaps) {}
void Register(RG::RenderGraph& rg) override;
@@ -32,7 +33,7 @@ namespace Engine::Passes {
RG::TextureHandle m_HDRColor;
RG::TextureHandle m_Velocity; // TAA 运动向量(第二颜色附件)
RG::TextureHandle m_Depth;
RG::TextureHandle m_ShadowMap;
std::array<RG::TextureHandle, kMaxCascades> m_ShadowMaps;
};
} // namespace Engine::Passes
@@ -12,8 +12,10 @@
#define HELLOVULKAN_SCENEGPU_H
#include <Engine/Core/FrameContext.h>
#include <Engine/Renderer/SceneTypes.h>
#include <Engine/RenderGraph/RenderGraph.h>
#include <vulkan/vulkan.hpp>
#include <array>
#include <vector>
namespace Engine {
@@ -39,7 +41,7 @@ namespace Engine {
// ---- set 布局(shader 契约,改绑定必须同步 shaders/)----
vk::DescriptorSetLayout uboSetLayout; // Set0 = {UBO, ObjectBuffer, PrevObjectBuffer}
vk::DescriptorSetLayout textureSetLayout; // Set1 = {albedo}
vk::DescriptorSetLayout shadowSetLayout; // Set2 = {shadow map}
vk::DescriptorSetLayout shadowSetLayout; // Set2 = {级联 0/1/2 阴影图}
vk::PipelineLayout forwardLayout; // 3 sets,无 push(GPUDriven 经 SSBO)
vk::PipelineLayout shadowLayout; // Set0,push = lightVP 64B
@@ -55,7 +57,8 @@ namespace Engine {
vk::ShaderEXT shadowVert{}, shadowFrag{};
// RG 重编译后重绑阴影图(物理镜像按槽位各一份)
void BindShadowMap(RG::RenderGraph& rg, RG::TextureHandle shadowMap);
void BindShadowMaps(RG::RenderGraph& rg,
const std::array<RG::TextureHandle, kMaxCascades>& maps);
private:
Device& m_Device;
@@ -1,8 +1,9 @@
//
// Created by atdunbg on 2026/9/23.
//
// ShadowPass:光源视角写深度。GPUDriven batch 消费光源视锥剔除结果
// (FrameSceneData::shadowView——相机外实体仍投影子)。
// ShadowPass:CSM 级联阴影。单 pass 渲 kMaxCascades 个 region,
// 每级联独立光源 VP(FrameSceneData::lightSpace[c],SceneRenderer 拟合),
// 消费光源视锥剔除结果(FrameSceneData::shadowView——相机外实体仍投影子)。
//
#ifndef HELLOVULKAN_SHADOWPASS_H
@@ -11,13 +12,15 @@
#include <Engine/Renderer/Passes/RenderPass.h>
#include <Engine/Renderer/Passes/SceneGpu.h>
#include <Engine/Renderer/SceneTypes.h>
#include <array>
namespace Engine::Passes {
class ShadowPass final : public RenderPass {
public:
ShadowPass(SceneGpu& gpu, FrameSceneData& frame, RG::TextureHandle shadowMap)
: m_Gpu(gpu), m_Frame(frame), m_ShadowMap(shadowMap) {}
ShadowPass(SceneGpu& gpu, FrameSceneData& frame,
const std::array<RG::TextureHandle, kMaxCascades>& shadowMaps)
: m_Gpu(gpu), m_Frame(frame), m_ShadowMaps(shadowMaps) {}
void Register(RG::RenderGraph& rg) override;
void OnTargetsRebuilt(RG::RenderGraph& rg) override;
@@ -25,7 +28,7 @@ namespace Engine::Passes {
private:
SceneGpu& m_Gpu;
FrameSceneData& m_Frame;
RG::TextureHandle m_ShadowMap;
std::array<RG::TextureHandle, kMaxCascades> m_ShadowMaps;
};
} // namespace Engine::Passes
+2 -1
View File
@@ -25,6 +25,7 @@
#include <Engine/Renderer/SceneTypes.h>
#include <Engine/RenderGraph/RenderGraph.h>
#include <vulkan/vulkan.hpp>
#include <array>
#include <vector>
namespace Engine {
@@ -125,7 +126,7 @@ namespace Engine {
// ---- RenderGraph 资源句柄 ----
RG::TextureHandle m_Backbuffer{};
RG::TextureHandle m_Depth{};
RG::TextureHandle m_ShadowMap{};
std::array<RG::TextureHandle, kMaxCascades> m_ShadowMaps{}; // CSM 级联
RG::TextureHandle m_HDRColor{}; // Forward 输出(线性 HDR),TAA 归一
RG::TextureHandle m_Velocity{}; // Forward 输出(clip 空间运动向量)
RG::TextureHandle m_TAAColor{}; // TAA 累积输出(自引用历史),后处理源
@@ -45,9 +45,10 @@ namespace Engine {
f32 GetCollectMs() const { return m_Frame.collectMs; }
private:
// 阴影光空间矩阵:光源正交投影(覆盖场景,[0,1] 深度重映射)。
// 写入(Shadow pass push)与采样(Forward UBO)共用同一矩阵。
void ComputeLightSpace(const glm::vec3& shadowDir);
// CSM:把相机视锥按实用分割切 N 段,每段独立拟合光源正交投影
//(含纹素对齐防阴影边缘闪烁)。写入(Shadow pass push)与采样
//(Forward UBO)共用同一组矩阵。
void ComputeCascades(const glm::vec3& shadowDir);
FrameSceneData m_Frame;
+6 -4
View File
@@ -34,7 +34,8 @@ namespace Engine {
constexpr u32 kMaxLights = 4;
constexpr u32 kMaxInstances = 16384; // 实例 SSBO 容量(矩阵数)
constexpr u32 kShadowMapSize = 2048; // 阴影图边长
constexpr u32 kMaxCascades = 3; // CSM 级联数
constexpr u32 kCascadeRes = 1024; // 每级联阴影图边长
// 镜像 shader 侧 UBO 布局的灯光项
struct LightData {
@@ -43,15 +44,16 @@ namespace Engine {
};
// 镜像 simple_shader 的 UBO 布局(std140 语义由 shader 侧对齐)。
// 字段只能追加在尾部(shadow.vert 共用本 set 前缀,前插会错位)。
// 字段顺序改动必须同步 simple_shader.vert/frag(shadow.vert 只用 SSBO)。
struct UniformBufferObject {
glm::mat4 view;
glm::mat4 proj; // 抖动后的投影(光栅化用)
glm::mat4 lightSpace; // 阴影光源 VP(写入与采样共用同一矩阵)
glm::mat4 lightSpace[kMaxCascades]; // CSM 各级联光空间 VP([0,1] 重映射)
glm::vec4 cascadeSplits; // xyz = 级联 0..2 远边界(视图深度,正值),w 未用
glm::vec4 ambientColor;
LightData lights[kMaxLights];
// ---- TAA(尾部追加;velocity 在 vertex 里算)----
// ---- TAA(velocity 在 vertex 里算)----
glm::mat4 projNoJitter; // 未抖动投影(velocity 当前帧 clip 用)
glm::mat4 prevView; // 上一帧视图(velocity)
glm::mat4 prevProj; // 上一帧未抖动投影(velocity)