feat: Shadow pass 接入 RenderGraph,支持 SRT 变换与程序化平面

阴影(本修正的核心根因):
- Shadow pass 光源正交写深度(2048 D32),Forward 采样比较;
  写入与采样共用同一 lightSpace 矩阵,杜绝姿态/镜像不一致
- 修复:push constant range 64B→128B(原 Shadow pass 推 {lightVP,model}
  超出 layout 声明范围 = UB,阴影中模型矩阵读到垃圾导致旋转错乱)
- 修复:ShadowFactor 缺少 NDC→UV 映射(*0.5+0.5),采样区域整体错位
- 修复:RG ShaderRead stage 位值直转(eFragment=0x10 在 PipelineStage2
  是 TESSELLATION_CONTROL),改为逐位显式映射
- Camera perspective 深度重映射到 Vulkan [0,1](公式与 ortho 不同:
  M22=M22*0.5-0.5, M32*=0.5);光源 ortho remap 用 M32=M32*0.5+0.5

引擎配套:
- TransformComponent 重构为 position/rotation/scale + Compose()
- Texture::CreateSolidColor / Model::CreatePlane 程序化资产
  (两段式上传公共路径),AssetManager.CreatePlane 带缓存入口
- RG 新增 GetPhysicalView(h, slot):按帧槽位把 RG 资源绑进外部描述符
  (阴影图 per-slot 物理镜像各自绑定)

验证:validation 0 error 0 warning,阴影随实体旋转正确
This commit is contained in:
2026-09-22 16:40:54 +08:00
parent 394bfd3d0c
commit 07a3b87001
17 changed files with 494 additions and 55 deletions
+33 -5
View File
@@ -562,6 +562,17 @@ namespace Engine::RG {
return img.release();
}
vk::ImageView RenderGraph::GetPhysicalView(TextureHandle h, u32 frameSlot) const
{
const auto& entry = GetEntry(h);
if (entry.imported) return entry.importedView;
if (frameSlot >= entry.physical.size() || !entry.physical[frameSlot]) {
ENGINE_FATAL("RenderGraph: no physical image for '{}' slot {} "
"(compile first?)", entry.name, frameSlot);
}
return entry.physical[frameSlot]->view;
}
// ---------- 执行 ----------
void RenderGraph::Execute(const FrameContext& frame)
@@ -679,11 +690,28 @@ namespace Engine::RG {
case ResourceUse::DepthAttachment:
return vk::PipelineStageFlagBits2::eEarlyFragmentTests |
vk::PipelineStageFlagBits2::eLateFragmentTests;
case ResourceUse::ShaderRead:
// ShaderStageFlags 与 PipelineStageFlags2 位值兼容,需显式转换
return vk::PipelineStageFlags2(
static_cast<VkPipelineStageFlags2>(
static_cast<VkShaderStageFlags>(use.readStage)));
case ResourceUse::ShaderRead: {
// ShaderStageFlags 与 PipelineStageFlags2 位值不同
//(eFragment=0x10 vs eFragmentShader=0x80),禁止位值直转,逐位映射
vk::PipelineStageFlags2 mapped{};
if (use.readStage & vk::ShaderStageFlagBits::eVertex)
mapped |= vk::PipelineStageFlagBits2::eVertexShader;
if (use.readStage & vk::ShaderStageFlagBits::eFragment)
mapped |= vk::PipelineStageFlagBits2::eFragmentShader;
if (use.readStage & vk::ShaderStageFlagBits::eCompute)
mapped |= vk::PipelineStageFlagBits2::eComputeShader;
if (use.readStage & vk::ShaderStageFlagBits::eGeometry)
mapped |= vk::PipelineStageFlagBits2::eGeometryShader;
if (use.readStage & vk::ShaderStageFlagBits::eTessellationControl)
mapped |= vk::PipelineStageFlagBits2::eTessellationControlShader;
if (use.readStage & vk::ShaderStageFlagBits::eTessellationEvaluation)
mapped |= vk::PipelineStageFlagBits2::eTessellationEvaluationShader;
if (!mapped) {
ENGINE_FATAL("RenderGraph: ShaderRead stage not mappable: {}",
static_cast<u32>(use.readStage));
}
return mapped;
}
case ResourceUse::TransferSrc:
case ResourceUse::TransferDst:
return vk::PipelineStageFlagBits2::eTransfer;