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:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -183,10 +183,13 @@ namespace Engine
|
||||
m_Depth = m_RenderGraph->CreateTexture(
|
||||
"MainDepth", RG::TextureDesc{depthFormat});
|
||||
|
||||
// 阴影图:固定尺寸正交深度图(D32 采样)
|
||||
m_ShadowMap = m_RenderGraph->CreateTexture(
|
||||
"ShadowMap",
|
||||
RG::TextureDesc{vk::Format::eD32Sfloat, kShadowMapSize, kShadowMapSize});
|
||||
// CSM 级联阴影图:每级联独立 1024² 正交深度图(D32 采样),
|
||||
// 范围由 SceneRenderer::ComputeCascades 按相机视锥切片拟合
|
||||
for (u32 c = 0; c < kMaxCascades; ++c) {
|
||||
m_ShadowMaps[c] = m_RenderGraph->CreateTexture(
|
||||
"ShadowMap" + std::to_string(c),
|
||||
RG::TextureDesc{vk::Format::eD32Sfloat, kCascadeRes, kCascadeRes});
|
||||
}
|
||||
|
||||
// HDR 颜色图:Forward 的输出(线性光照值 + 抖动),TAA 显式完成
|
||||
// 时域累积后写入 TAAColor(对齐 UE/Unity 管线)
|
||||
@@ -222,9 +225,9 @@ namespace Engine
|
||||
// 要加新 pass:new 一个 RenderPass 子类,在这里 push 一行。
|
||||
FrameSceneData& frame = m_SceneRenderer->Frame();
|
||||
m_Passes.push_back(CreateScope<Passes::ShadowPass>(
|
||||
*m_SceneGpu, frame, m_ShadowMap));
|
||||
*m_SceneGpu, frame, m_ShadowMaps));
|
||||
m_Passes.push_back(CreateScope<Passes::ForwardPass>(
|
||||
*m_SceneGpu, frame, m_HDRColor, m_Velocity, m_Depth, m_ShadowMap));
|
||||
*m_SceneGpu, frame, m_HDRColor, m_Velocity, m_Depth, m_ShadowMaps));
|
||||
m_Passes.push_back(CreateScope<Passes::TAAPass>(
|
||||
*m_FullscreenGpu, /*historyAlpha*/ 0.9f, m_HDRColor, m_Velocity,
|
||||
m_TAAColor));
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
|
||||
namespace Engine
|
||||
{
|
||||
@@ -23,11 +24,11 @@ namespace Engine
|
||||
|
||||
namespace
|
||||
{
|
||||
// 光源正交包围盒(覆盖当前场景;后续按场景 AABB 自适应)
|
||||
constexpr f32 kLightOrthoExtent = 15.0f;
|
||||
constexpr f32 kLightDistance = 30.0f;
|
||||
constexpr f32 kLightNear = 1.0f;
|
||||
constexpr f32 kLightFar = 80.0f;
|
||||
// 相机裁剪范围(与 BuildFrame 中 GetProjection 的参数一致)
|
||||
constexpr f32 kCameraNear = 0.1f;
|
||||
constexpr f32 kCameraFar = 100.0f;
|
||||
// 实用分割插值因子(0=均匀,1=对数;0.6 是业界常用值)
|
||||
constexpr f32 kCascadeLambda = 0.6f;
|
||||
|
||||
// Halton 低差异序列(TAA 子像素抖动)
|
||||
f32 Halton(u32 index, u32 base)
|
||||
@@ -42,26 +43,80 @@ namespace Engine
|
||||
}
|
||||
}
|
||||
|
||||
void SceneRenderer::ComputeLightSpace(const glm::vec3& shadowDir)
|
||||
void SceneRenderer::ComputeCascades(const glm::vec3& shadowDir)
|
||||
{
|
||||
// 光源位置在光方向的反方向远处,看向场景中心
|
||||
const glm::vec3 lightPos = -shadowDir * kLightDistance;
|
||||
// up 与视线平行时叉积退化(NaN),切换 up
|
||||
constexpr u32 N = kMaxCascades;
|
||||
|
||||
// ---- 1. 实用分割:均匀与对数分割按 λ 插值(视图深度,正值)----
|
||||
f32 splits[N];
|
||||
for (u32 i = 1; i <= N; ++i) {
|
||||
const f32 t = static_cast<f32>(i) / N;
|
||||
const f32 uniform = kCameraNear + (kCameraFar - kCameraNear) * t;
|
||||
const f32 log = kCameraNear * glm::pow(kCameraFar / kCameraNear, t);
|
||||
splits[i - 1] = glm::mix(uniform, log, kCascadeLambda);
|
||||
}
|
||||
for (u32 c = 0; c < N; ++c) {
|
||||
m_Frame.ubo.cascadeSplits[c] = splits[c];
|
||||
}
|
||||
|
||||
// ---- 2. 全视锥 8 角反投影到世界(NDC z: 0=近 1=远,Vulkan [0,1])----
|
||||
const glm::mat4 invVP = glm::inverse(m_Frame.ubo.projNoJitter * m_Frame.ubo.view);
|
||||
glm::vec3 nearC[4], farC[4];
|
||||
const glm::vec2 ndcXY[4] = {{-1,-1},{1,-1},{-1,1},{1,1}};
|
||||
for (u32 i = 0; i < 4; ++i) {
|
||||
glm::vec4 p = invVP * glm::vec4(ndcXY[i], 0.0f, 1.0f);
|
||||
nearC[i] = glm::vec3(p / p.w);
|
||||
p = invVP * glm::vec4(ndcXY[i], 1.0f, 1.0f);
|
||||
farC[i] = glm::vec3(p / p.w);
|
||||
}
|
||||
|
||||
// ---- 3. 每级联:切片角点 → 光空间 AABB 拟合 → 纹素对齐 ----
|
||||
const glm::vec3 up = glm::abs(shadowDir.y) > 0.999f
|
||||
? glm::vec3(0.0f, 0.0f, 1.0f) : glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
const glm::mat4 lightView = glm::lookAt(
|
||||
lightPos, glm::vec3(0.0f), up);
|
||||
|
||||
glm::mat4 lightProj = glm::ortho(
|
||||
-kLightOrthoExtent, kLightOrthoExtent,
|
||||
-kLightOrthoExtent, kLightOrthoExtent,
|
||||
kLightNear, kLightFar);
|
||||
// Vulkan clip 深度 [0,1]:z' = 0.5*z + 0.5*w。
|
||||
// ortho 的 w_clip = 1,偏移折算进 z 的平移分量(与 perspective 公式不同!)
|
||||
lightProj[2][2] *= 0.5f;
|
||||
lightProj[3][2] = lightProj[3][2] * 0.5f + 0.5f;
|
||||
for (u32 c = 0; c < N; ++c) {
|
||||
const f32 d0 = (c == 0) ? kCameraNear : splits[c - 1];
|
||||
const f32 d1 = splits[c];
|
||||
const f32 t0 = (d0 - kCameraNear) / (kCameraFar - kCameraNear);
|
||||
const f32 t1 = (d1 - kCameraNear) / (kCameraFar - kCameraNear);
|
||||
|
||||
m_Frame.ubo.lightSpace = lightProj * lightView;
|
||||
glm::vec3 slice[8];
|
||||
for (u32 i = 0; i < 4; ++i) {
|
||||
slice[i] = glm::mix(nearC[i], farC[i], t0);
|
||||
slice[4 + i] = glm::mix(nearC[i], farC[i], t1);
|
||||
}
|
||||
|
||||
// 光视图:位于切片中心,沿光方向看
|
||||
glm::vec3 center(0.0f);
|
||||
for (const auto& p : slice) center += p;
|
||||
center *= (1.0f / 8.0f);
|
||||
const glm::mat4 lightView = glm::lookAt(center, center + shadowDir, up);
|
||||
|
||||
glm::vec3 mn( std::numeric_limits<f32>::max());
|
||||
glm::vec3 mx( std::numeric_limits<f32>::lowest());
|
||||
for (const auto& p : slice) {
|
||||
const glm::vec3 lv = glm::vec3(lightView * glm::vec4(p, 1.0f));
|
||||
mn = glm::min(mn, lv);
|
||||
mx = glm::max(mx, lv);
|
||||
}
|
||||
|
||||
// 纹素对齐:X/Y 边界量化到纹素网格(消除相机移动时的阴影边缘闪烁)
|
||||
const f32 texelX = (mx.x - mn.x) / static_cast<f32>(kCascadeRes);
|
||||
const f32 texelY = (mx.y - mn.y) / static_cast<f32>(kCascadeRes);
|
||||
mn.x = glm::floor(mn.x / texelX) * texelX;
|
||||
mn.y = glm::floor(mn.y / texelY) * texelY;
|
||||
mx.x = mn.x + glm::ceil((mx.x - mn.x) / texelX) * texelX;
|
||||
mx.y = mn.y + glm::ceil((mx.y - mn.y) / texelY) * texelY;
|
||||
|
||||
// 光视图空间里 z 为负(看向 -Z);近远距离取正
|
||||
glm::mat4 lightProj = glm::ortho(mn.x, mx.x, mn.y, mx.y,
|
||||
-mx.z, -mn.z);
|
||||
// Vulkan clip 深度 [0,1](ortho 的 w_clip=1,偏移折进 z 平移分量)
|
||||
lightProj[2][2] *= 0.5f;
|
||||
lightProj[3][2] = lightProj[3][2] * 0.5f + 0.5f;
|
||||
|
||||
m_Frame.ubo.lightSpace[c] = lightProj * lightView;
|
||||
}
|
||||
}
|
||||
|
||||
bool SceneRenderer::BuildFrame(ECS::Scene& scene, Assets::AssetManager& assets,
|
||||
@@ -121,7 +176,7 @@ namespace Engine
|
||||
ubo.lights[i].direction = glm::vec4(0.0f);
|
||||
ubo.lights[i].color = glm::vec4(0.0f);
|
||||
}
|
||||
ComputeLightSpace(shadowDir);
|
||||
ComputeCascades(shadowDir);
|
||||
|
||||
// ---- 3. GPUDriven:单趟收集 + 双视图剔除 ----
|
||||
// 每个实例对相机视锥、光源视锥分别做包围球测试:
|
||||
@@ -140,9 +195,11 @@ namespace Engine
|
||||
bool instanceTruncated = false;
|
||||
|
||||
// 剔除平面提取按 [0,1] 深度约定——相机 proj 与 lightSpace 都已
|
||||
// 做 [0,1] 重映射;原始 [-1,1] 矩阵的 near 平面公式不同,勿用
|
||||
// 做 [0,1] 重映射;原始 [-1,1] 矩阵的 near 平面公式不同,勿用。
|
||||
// 光源视锥用最远级联(覆盖全阴影范围)
|
||||
const Frustum mainFrustum = Frustum::FromMatrix(ubo.proj * ubo.view);
|
||||
const Frustum shadowFrustum = Frustum::FromMatrix(ubo.lightSpace);
|
||||
const Frustum shadowFrustum =
|
||||
Frustum::FromMatrix(ubo.lightSpace[kMaxCascades - 1]);
|
||||
|
||||
scene.Each<TransformComponent, MeshRendererComponent>(
|
||||
[&](Entity entity, TransformComponent& t, MeshRendererComponent& mr) {
|
||||
|
||||
Reference in New Issue
Block a user