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
+28 -10
View File
@@ -28,8 +28,31 @@ namespace Engine
return;
}
m_Width = static_cast<u32>(width);
m_Height = static_cast<u32>(height);
InitFromPixels(pixels, static_cast<u32>(width), static_cast<u32>(height));
stbi_image_free(pixels);
ENGINE_INFO("Texture loaded: {} ({}x{})", path, m_Width, m_Height);
}
Texture::Texture(Device& device, MemoryAllocator& allocator, ProceduralTag,
const std::array<u8, 4>& rgba)
: m_Device(device) , m_Allocator(allocator)
{
const u8 pixels[4] = { rgba[0], rgba[1], rgba[2], rgba[3] };
InitFromPixels(pixels, 1, 1);
}
Scope<Texture> Texture::CreateSolidColor(Device& device, MemoryAllocator& allocator,
u8 r, u8 g, u8 b)
{
return Scope<Texture>(new Texture(device, allocator, ProceduralTag{}, {r, g, b, 255}));
}
void Texture::InitFromPixels(const void* pixels, u32 width, u32 height)
{
m_Width = width;
m_Height = height;
vk::DeviceSize imageSize = static_cast<vk::DeviceSize>(width) *
static_cast<vk::DeviceSize>(height) * 4; // RGBA8
@@ -42,7 +65,7 @@ namespace Engine
vk::Buffer stagingBuffer = m_Device.GetDevice().createBuffer(stagingInfo);
auto stagingReqs = m_Device.GetDevice().getBufferMemoryRequirements(stagingBuffer);
auto stagingAlloc = allocator.Allocate(
auto stagingAlloc = m_Allocator.Allocate(
stagingReqs,
vk::MemoryPropertyFlagBits::eHostVisible |
vk::MemoryPropertyFlagBits::eHostCoherent);
@@ -52,8 +75,6 @@ namespace Engine
// 3. 拷贝像素到 Staging Buffer
std::memcpy(stagingAlloc.MappedPtr, pixels, static_cast<size_t>(imageSize));
stbi_image_free(pixels);
// 4. 创建 Device Local Image
vk::ImageCreateInfo imageInfo;
imageInfo.setImageType(vk::ImageType::e2D)
@@ -70,7 +91,7 @@ namespace Engine
m_Image = m_Device.GetDevice().createImage(imageInfo);
auto imageReqs = m_Device.GetDevice().getImageMemoryRequirements(m_Image);
auto imageAlloc = allocator.Allocate(imageReqs, vk::MemoryPropertyFlagBits::eDeviceLocal);
auto imageAlloc = m_Allocator.Allocate(imageReqs, vk::MemoryPropertyFlagBits::eDeviceLocal);
m_ImageAllocation = imageAlloc;
@@ -185,12 +206,9 @@ namespace Engine
m_Device.GetDevice().destroyFence(fence);
m_Device.GetDevice().destroyCommandPool(pool);
m_Device.GetDevice().destroyBuffer(stagingBuffer);
allocator.Free(stagingAlloc);
m_Allocator.Free(stagingAlloc);
// staging 内存由 MemoryAllocator 统一释放
ENGINE_INFO("Texture loaded: {} ({}x{})", path, m_Width, m_Height);
// 显存占用 = 宽 * 高 * 4 字节(RGBA8)
m_TextureBytes = static_cast<u64>(m_Width) * m_Height * 4;
STATS.AddTexture(m_TextureBytes);