feat: Shadow pass——主光方向光阴影(RenderGraph 双 pass 管线首个验证案例)
- RenderGraph 声明 ShadowMap 虚拟资源(D32,2048²固定分辨率,独立于 swapchain 尺寸),Shadow pass 写深度 → Forward 声明 ShaderRead 采样, barrier/layout(DepthAttachment→ShaderReadOnly)全部由图自动预算 - Shadow pass:空 fragment shader + 深度偏移(斜率缩放)抗 shadow acne - Forward:UBO 增加 lightSpace 矩阵;Set 2 采样阴影图,手动对比 + 斜率缩放偏差;clamp-to-border 白色边界(光视锥外 = 全亮) - 光空间矩阵:主光方向反推 30 单位正交盒;深度重映射 [-1,1]→[0,1] - GetPhysicalView(slot):物理镜像按帧槽位隔离,描述符 Compile 后一次性写入 - 修复 RG 隐蔽 bug:ShaderStage 与 PipelineStage2 位值不同 (eFragment=0x10 vs eFragmentShader=0x80),位值直转会撞出 TESSELLATION_CONTROL stage;改为逐位显式映射 - 修复 Camera 投影深度未重映射到 [0,1] 的潜在裁剪问题 validation 零错误零警告(3 passes / 5 barriers / 4 gpu images)
This commit is contained in:
@@ -220,6 +220,10 @@ namespace Engine::RG {
|
||||
// 每帧执行:按编译好的顺序录命令。要求 cmd 处于 recording 状态。
|
||||
void Execute(const FrameContext& frame);
|
||||
|
||||
// 物理镜像访问器:给"按帧槽位预写描述符"的场景用(如阴影深度图采样)。
|
||||
// 仅 created 资源有效;imported 资源(swapchain)每帧才绑定,无物理视图。
|
||||
vk::ImageView GetPhysicalView(TextureHandle h, u32 frameSlot) const;
|
||||
|
||||
// ---- 统计 ----
|
||||
|
||||
u32 GetPassCount() const;
|
||||
|
||||
@@ -56,6 +56,7 @@ namespace Engine {
|
||||
private:
|
||||
void BuildGraph(); // 声明 pass 结构 + 首次 Compile
|
||||
void HandleResize();
|
||||
void UpdateShadowDescriptors(); // Compile 后把阴影深度图物理视图写进 Set 2
|
||||
|
||||
Window& m_Window;
|
||||
|
||||
@@ -78,9 +79,24 @@ namespace Engine {
|
||||
vk::ShaderEXT m_VertShader{};
|
||||
vk::ShaderEXT m_FragShader{};
|
||||
|
||||
// ---- 阴影 ----
|
||||
// Shadow pass 只绑定顶点 shader(深度写入,无颜色输出),
|
||||
// 独立 pipeline layout:空描述符 + 128B push(lightVP + model)
|
||||
vk::PipelineLayout m_ShadowPipelineLayout;
|
||||
vk::ShaderEXT m_ShadowVertShader{};
|
||||
vk::ShaderEXT m_ShadowFragShader{};
|
||||
|
||||
// Set 2:阴影深度图采样。每槽位一组描述符,Compile 后用
|
||||
// GetPhysicalView(slot) 一次性写入(物理镜像按槽位隔离,无需每帧更新)
|
||||
vk::DescriptorSetLayout m_ShadowSetLayout;
|
||||
vk::Sampler m_ShadowSampler;
|
||||
std::vector<vk::DescriptorSet> m_ShadowDescriptorSets; // 每帧槽位一个
|
||||
glm::mat4 m_LightSpaceMatrix{1.0f}; // 每帧按主光方向重算
|
||||
|
||||
// ---- RenderGraph 资源句柄 ----
|
||||
RG::TextureHandle m_Backbuffer{};
|
||||
RG::TextureHandle m_Depth{};
|
||||
RG::TextureHandle m_ShadowMap{};
|
||||
|
||||
// ---- 当帧绑定(Render() 期间有效,供 pass 回调读取)----
|
||||
ECS::Scene* m_BoundScene = nullptr;
|
||||
|
||||
@@ -612,6 +612,15 @@ namespace Engine::RG {
|
||||
|
||||
// ---------- RenderGraphContext ----------
|
||||
|
||||
vk::ImageView RenderGraph::GetPhysicalView(TextureHandle h, u32 frameSlot) const
|
||||
{
|
||||
const TextureEntry& entry = GetEntry(h);
|
||||
if (entry.imported) {
|
||||
return entry.importedView; // 导入资源回退到当帧绑定视图
|
||||
}
|
||||
return entry.physical[frameSlot]->view;
|
||||
}
|
||||
|
||||
vk::ImageView RenderGraphContext::View(TextureHandle h) const
|
||||
{
|
||||
const auto& entry = graph->GetEntry(h);
|
||||
@@ -679,11 +688,25 @@ 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: {
|
||||
// 关键:ShaderStage 与 PipelineStage2 的位值完全不同!
|
||||
// ShaderStage::eFragment = 0x10,PipelineStage2::eFragmentShader = 0x80
|
||||
// 位值直转会变成完全错误的 stage(0x10 在 Pipeline2 里是
|
||||
// TESSELLATION_CONTROL)。必须逐位显式映射。
|
||||
const vk::ShaderStageFlags stages = use.readStage;
|
||||
vk::PipelineStageFlags2 out{};
|
||||
auto mapStage = [&](vk::ShaderStageFlagBits from,
|
||||
vk::PipelineStageFlagBits2 to) {
|
||||
if (stages & from) out |= to;
|
||||
};
|
||||
mapStage(vk::ShaderStageFlagBits::eVertex, vk::PipelineStageFlagBits2::eVertexShader);
|
||||
mapStage(vk::ShaderStageFlagBits::eTessellationControl, vk::PipelineStageFlagBits2::eTessellationControlShader);
|
||||
mapStage(vk::ShaderStageFlagBits::eTessellationEvaluation, vk::PipelineStageFlagBits2::eTessellationEvaluationShader);
|
||||
mapStage(vk::ShaderStageFlagBits::eGeometry, vk::PipelineStageFlagBits2::eGeometryShader);
|
||||
mapStage(vk::ShaderStageFlagBits::eFragment, vk::PipelineStageFlagBits2::eFragmentShader);
|
||||
mapStage(vk::ShaderStageFlagBits::eCompute, vk::PipelineStageFlagBits2::eComputeShader);
|
||||
return out;
|
||||
}
|
||||
case ResourceUse::TransferSrc:
|
||||
case ResourceUse::TransferDst:
|
||||
return vk::PipelineStageFlagBits2::eTransfer;
|
||||
|
||||
@@ -40,14 +40,23 @@ namespace Engine
|
||||
struct UniformBufferObject {
|
||||
glm::mat4 view;
|
||||
glm::mat4 proj;
|
||||
glm::mat4 lightSpace; // 主光(lights[0])光空间变换(阴影用)
|
||||
glm::vec4 ambientColor;
|
||||
Light lights[4];
|
||||
};
|
||||
|
||||
// Shadow pass 推送块:lightVP + model = 128B(最小保证上限内)
|
||||
struct ShadowPushConstants {
|
||||
glm::mat4 lightVP;
|
||||
glm::mat4 model;
|
||||
};
|
||||
|
||||
constexpr u32 kUboBinding = 0;
|
||||
constexpr u32 kUBOSetIndex = 0;
|
||||
constexpr u32 kTextureSetIndex = 1;
|
||||
constexpr u32 kShadowSetIndex = 2;
|
||||
constexpr u32 kMaxLights = 4;
|
||||
constexpr u32 kShadowMapSize = 2048; // 固定分辨率(独立于 swapchain 尺寸)
|
||||
}
|
||||
|
||||
Renderer::Renderer(Window& window)
|
||||
@@ -75,9 +84,16 @@ namespace Engine
|
||||
};
|
||||
m_TextureSetLayout = m_DescriptorManager->CreateLayout(texBindings);
|
||||
|
||||
// 6. PipelineLayout:两个 set
|
||||
// 5.5 Set 2: 阴影深度图(主 shader 采样对比)
|
||||
std::vector<DescriptorBinding> shadowBindings = {
|
||||
{0, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment},
|
||||
};
|
||||
m_ShadowSetLayout = m_DescriptorManager->CreateLayout(shadowBindings);
|
||||
|
||||
// 6. PipelineLayout:三个 set(主 shader 64B push;阴影 shader 单独 128B)
|
||||
m_PipelineLayout = m_DescriptorManager->CreatePipelineLayout(
|
||||
{m_UBOSetLayout, m_TextureSetLayout}, sizeof(glm::mat4));
|
||||
{m_UBOSetLayout, m_TextureSetLayout, m_ShadowSetLayout},
|
||||
sizeof(glm::mat4));
|
||||
|
||||
// 7. ShaderManager(VK_EXT_shader_object)
|
||||
m_ShaderManager = CreateScope<ShaderManager>(*m_Device);
|
||||
@@ -86,19 +102,53 @@ namespace Engine
|
||||
auto vert = m_ShaderManager->CreateFromFile(
|
||||
shaderDir + "simple_shader.vert.spv",
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
{m_UBOSetLayout, m_TextureSetLayout}, sizeof(glm::mat4));
|
||||
{m_UBOSetLayout, m_TextureSetLayout, m_ShadowSetLayout},
|
||||
sizeof(glm::mat4));
|
||||
auto frag = m_ShaderManager->CreateFromFile(
|
||||
shaderDir + "simple_shader.frag.spv",
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
{m_UBOSetLayout, m_TextureSetLayout}, sizeof(glm::mat4));
|
||||
{m_UBOSetLayout, m_TextureSetLayout, m_ShadowSetLayout},
|
||||
sizeof(glm::mat4));
|
||||
|
||||
m_VertShader = vert.Handle;
|
||||
m_FragShader = frag.Handle;
|
||||
|
||||
if (!m_VertShader || !m_FragShader) {
|
||||
// 阴影 shader:无描述符,128B push(lightVP + model)
|
||||
m_ShadowPipelineLayout = m_DescriptorManager->CreatePipelineLayout(
|
||||
std::vector<vk::DescriptorSetLayout>{}, sizeof(ShadowPushConstants));
|
||||
auto shadowVert = m_ShaderManager->CreateFromFile(
|
||||
shaderDir + "shadow.vert.spv",
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
{}, sizeof(ShadowPushConstants));
|
||||
auto shadowFrag = m_ShaderManager->CreateFromFile(
|
||||
shaderDir + "shadow.frag.spv",
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
{}, sizeof(ShadowPushConstants));
|
||||
|
||||
m_ShadowVertShader = shadowVert.Handle;
|
||||
m_ShadowFragShader = shadowFrag.Handle;
|
||||
|
||||
if (!m_VertShader || !m_FragShader || !m_ShadowVertShader || !m_ShadowFragShader) {
|
||||
ENGINE_FATAL("Failed to create shader objects");
|
||||
}
|
||||
|
||||
// 阴影采样器:clamp-to-border + 白色边界(视锥外 = 全亮),
|
||||
// 手动对比而非 comparison sampler,逻辑清晰且无 format feature 依赖
|
||||
vk::SamplerCreateInfo samplerInfo;
|
||||
samplerInfo.setMagFilter(vk::Filter::eLinear)
|
||||
.setMinFilter(vk::Filter::eLinear)
|
||||
.setMipmapMode(vk::SamplerMipmapMode::eNearest)
|
||||
.setAddressModeU(vk::SamplerAddressMode::eClampToBorder)
|
||||
.setAddressModeV(vk::SamplerAddressMode::eClampToBorder)
|
||||
.setAddressModeW(vk::SamplerAddressMode::eClampToBorder)
|
||||
.setMipLodBias(0.0f)
|
||||
.setAnisotropyEnable(VK_FALSE)
|
||||
.setCompareEnable(VK_FALSE)
|
||||
.setMinLod(0.0f)
|
||||
.setMaxLod(0.0f)
|
||||
.setBorderColor(vk::BorderColor::eFloatOpaqueWhite);
|
||||
m_ShadowSampler = m_Device->GetDevice().createSampler(samplerInfo);
|
||||
|
||||
// 8. Swapchain(只管呈现;深度图由 RenderGraph 创建)
|
||||
m_Swapchain = CreateScope<Swapchain>(*m_Device, *m_Allocator, m_Window);
|
||||
m_Extent = m_Swapchain->GetExtent();
|
||||
@@ -121,6 +171,15 @@ namespace Engine
|
||||
m_UniformBuffer->Get(slot), m_UniformBuffer->GetSize());
|
||||
}
|
||||
|
||||
// 10.5 阴影深度图描述符:每槽位一组。
|
||||
// Compile 后由 UpdateShadowDescriptors 写入物理视图(槽位间隔离,
|
||||
// 物理镜像不重建则无需再更新——轮换重编译时会重新调用)。
|
||||
m_ShadowDescriptorSets.resize(kMaxFramesInFlight);
|
||||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||
m_ShadowDescriptorSets[slot] =
|
||||
m_DescriptorManager->AllocateDescriptorSet(m_ShadowSetLayout);
|
||||
}
|
||||
|
||||
// 11. RenderGraph:声明 + 编译(场景在 Render(Scene&) 时按帧绑定)
|
||||
BuildGraph();
|
||||
|
||||
@@ -137,10 +196,34 @@ namespace Engine
|
||||
if (m_FragShader) {
|
||||
m_Device->GetDevice().destroyShaderEXT(m_FragShader);
|
||||
}
|
||||
if (m_ShadowVertShader) {
|
||||
m_Device->GetDevice().destroyShaderEXT(m_ShadowVertShader);
|
||||
}
|
||||
if (m_ShadowFragShader) {
|
||||
m_Device->GetDevice().destroyShaderEXT(m_ShadowFragShader);
|
||||
}
|
||||
|
||||
m_Device->GetDevice().destroyPipelineLayout(m_PipelineLayout);
|
||||
m_Device->GetDevice().destroyPipelineLayout(m_ShadowPipelineLayout);
|
||||
m_Device->GetDevice().destroyDescriptorSetLayout(m_UBOSetLayout);
|
||||
m_Device->GetDevice().destroyDescriptorSetLayout(m_TextureSetLayout);
|
||||
m_Device->GetDevice().destroyDescriptorSetLayout(m_ShadowSetLayout);
|
||||
m_Device->GetDevice().destroySampler(m_ShadowSampler);
|
||||
}
|
||||
|
||||
void Renderer::UpdateShadowDescriptors()
|
||||
{
|
||||
// 把阴影深度图各槽位的物理视图写进 Set 2。
|
||||
// 必须在 Compile 之后调用(物理镜像此时才存在);
|
||||
// swapchain 轮换重编译会重建物理镜像,因此 HandleResize 也要重新调用。
|
||||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||
m_DescriptorManager->UpdateTexture(
|
||||
m_ShadowDescriptorSets[slot], 0,
|
||||
m_ShadowSampler,
|
||||
m_RenderGraph->GetPhysicalView(m_ShadowMap, slot));
|
||||
}
|
||||
|
||||
ENGINE_INFO("Shadow descriptors updated ({} slots)", kMaxFramesInFlight);
|
||||
}
|
||||
|
||||
void Renderer::BuildGraph()
|
||||
@@ -162,6 +245,102 @@ namespace Engine
|
||||
m_Depth = m_RenderGraph->CreateTexture(
|
||||
"MainDepth", RG::TextureDesc{depthFormat});
|
||||
|
||||
// 阴影深度图:固定分辨率(独立于 swapchain 尺寸),
|
||||
// usage 由 uses 自动推导:DepthAttachment | Sampled
|
||||
m_ShadowMap = m_RenderGraph->CreateTexture(
|
||||
"ShadowMap",
|
||||
RG::TextureDesc{vk::Format::eD32Sfloat, kShadowMapSize, kShadowMapSize});
|
||||
|
||||
// ---- Shadow pass:主光视角写深度 ----
|
||||
// 只写深度、无颜色附件;只绑顶点 shader 也可以,这里同样绑了
|
||||
// 空 fragment 以最大化驱动兼容性。
|
||||
m_RenderGraph->AddPass("Shadow",
|
||||
[this](RG::RenderGraphContext& ctx) {
|
||||
const vk::CommandBuffer cmd = ctx.cmd;
|
||||
|
||||
vk::ClearValue clearValue;
|
||||
clearValue.setDepthStencil(vk::ClearDepthStencilValue(1.0f, 0));
|
||||
|
||||
vk::RenderingAttachmentInfo depthAttachment;
|
||||
depthAttachment.setImageView(ctx.View(m_ShadowMap))
|
||||
.setImageLayout(vk::ImageLayout::eDepthAttachmentOptimal)
|
||||
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
||||
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
||||
.setClearValue(clearValue);
|
||||
|
||||
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_ShadowVertShader, m_ShadowFragShader};
|
||||
cmd.bindShadersEXT(stages, shaders);
|
||||
|
||||
// ---- 动态状态(阴影 pass 自带一套,与 Forward 无关)----
|
||||
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);
|
||||
|
||||
// 深度偏移:缓解 shadow acne(斜率缩放)
|
||||
cmd.setDepthBiasEnable(VK_TRUE);
|
||||
cmd.setDepthBias(1.25f, 0.0f, 1.75f);
|
||||
|
||||
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);
|
||||
|
||||
// ---- 绘制:主光视角下的全部渲染体 ----
|
||||
m_BoundScene->Each<TransformComponent, MeshRendererComponent>(
|
||||
[&](Entity, TransformComponent& transform,
|
||||
MeshRendererComponent& meshRenderer) {
|
||||
Model* model = m_BoundAssets->GetModel(meshRenderer.model);
|
||||
if (!model) return;
|
||||
|
||||
ShadowPushConstants push{m_LightSpaceMatrix, transform.world};
|
||||
cmd.pushConstants(m_ShadowPipelineLayout,
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
0, sizeof(ShadowPushConstants), &push);
|
||||
|
||||
for (auto& mesh : model->GetMeshes()) {
|
||||
mesh->Draw(cmd); // 阴影 pass 不需要材质描述符
|
||||
}
|
||||
});
|
||||
|
||||
cmd.endRendering();
|
||||
})
|
||||
.Write(m_ShadowMap, RG::ResourceUse::DepthAttachment);
|
||||
|
||||
// ---- Forward pass:直接渲染到 swapchain ----
|
||||
// 场景数据每帧经 m_BoundScene 从 ECS 读取
|
||||
m_RenderGraph->AddPass("Forward",
|
||||
@@ -203,10 +382,13 @@ namespace Engine
|
||||
std::array<vk::ShaderEXT, 2> shaders = {m_VertShader, m_FragShader};
|
||||
cmd.bindShadersEXT(stages, shaders);
|
||||
|
||||
// Set 0:本帧槽位的 UBO
|
||||
// Set 0:本帧槽位的 UBO;Set 2:本帧槽位的阴影深度图
|
||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||
m_PipelineLayout, kUBOSetIndex,
|
||||
m_UBODescriptorSets[ctx.frame.frameSlot], {});
|
||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||
m_PipelineLayout, kShadowSetIndex,
|
||||
m_ShadowDescriptorSets[ctx.frame.frameSlot], {});
|
||||
|
||||
// ---- 动态状态 ----
|
||||
cmd.setRasterizerDiscardEnable(VK_FALSE);
|
||||
@@ -287,7 +469,10 @@ namespace Engine
|
||||
cmd.endRendering();
|
||||
})
|
||||
.Write(m_Backbuffer, RG::ResourceUse::ColorAttachment)
|
||||
.Write(m_Depth, RG::ResourceUse::DepthAttachment);
|
||||
.Write(m_Depth, RG::ResourceUse::DepthAttachment)
|
||||
// 采样阴影深度图:RG 自动在 Shadow(写) → Forward(读) 之间
|
||||
// 插入 eDepthAttachmentOptimal → eShaderReadOnlyOptimal 的 barrier
|
||||
.Read(m_ShadowMap, RG::ResourceUse::ShaderRead);
|
||||
|
||||
// ---- Present pass:transition 到 ePresentSrcKHR ----
|
||||
m_RenderGraph->AddPass("Present",
|
||||
@@ -298,6 +483,7 @@ namespace Engine
|
||||
.Read(m_Backbuffer, RG::ResourceUse::Present);
|
||||
|
||||
m_RenderGraph->SetTargetExtent(m_Extent); // 触发首次 Compile
|
||||
UpdateShadowDescriptors(); // 物理镜像就位,写 Set 2
|
||||
}
|
||||
|
||||
void Renderer::HandleResize()
|
||||
@@ -311,8 +497,9 @@ namespace Engine
|
||||
m_SyncManager = CreateScope<SyncManager>(
|
||||
*m_Device, kMaxFramesInFlight, m_Swapchain->GetImageCount());
|
||||
|
||||
// RG 重编译:物理镜像按新尺寸轮换
|
||||
// RG 重编译:物理镜像按新尺寸轮换(阴影图是固定尺寸,会被重建)
|
||||
m_RenderGraph->SetTargetExtent(m_Extent);
|
||||
UpdateShadowDescriptors();
|
||||
}
|
||||
|
||||
void Renderer::Render(ECS::Scene& scene, Assets::AssetManager& assets)
|
||||
@@ -359,6 +546,25 @@ namespace Engine
|
||||
ubo.lights[i].color = glm::vec4(0.0f);
|
||||
}
|
||||
|
||||
// 1.5 光空间矩阵:主光(lights[0])视角的正交盒包住场景。
|
||||
// 光源"位置" = 光方向反向拉远 30 个单位,看向原点。
|
||||
// 深度重映射 [-1,1] → [0,1](Vulkan clip volume),与相机投影同理。
|
||||
glm::vec3 lightDir(ubo.lights[0].direction);
|
||||
if (glm::length(lightDir) > 0.0f) {
|
||||
glm::mat4 lightView = glm::lookAt(
|
||||
lightDir * 30.0f, // 沿光方向反推的光源位置
|
||||
glm::vec3(0.0f), // 看向原点
|
||||
glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
glm::mat4 lightProj = glm::ortho(
|
||||
-20.0f, 20.0f, -20.0f, 20.0f, 0.1f, 100.0f);
|
||||
|
||||
lightProj[2][2] = lightProj[2][2] * 0.5f + 0.5f;
|
||||
lightProj[3][2] = lightProj[3][2] * 0.5f + 0.5f;
|
||||
|
||||
m_LightSpaceMatrix = lightProj * lightView;
|
||||
}
|
||||
ubo.lightSpace = m_LightSpaceMatrix;
|
||||
|
||||
// 此时本槽位的 fence 尚未 reset——先等它(保证 GPU 已完成本槽位上一轮),
|
||||
// 再写 UBO,写-读竞争关闭。
|
||||
m_SyncManager->WaitFence(frameSlot);
|
||||
|
||||
Reference in New Issue
Block a user