fix: 阴影不可见——灯序依赖遍历顺序 + 光比失衡,新增帧截图调试能力

- 灯序修复(根因):entt Each 遍历序不保证创建序,lights[0] 曾落到
  点光(params.x=1)→ shader 阴影分支整个跳过。收集后 stable_sort
  方向光排前,确立"lights[0] = CSM 阴影光源"契约
- 光比失衡:IBL 环境与点光淹没主光阴影。KeyLight 5.0 + exposure
  默认 0.6 + ambientColor 0.07;阴影区环境光同步衰减(简化遮蔽)
- ShadowPass depth bias 0.006(acne 均值形态 vs 推过头丢阴影的折中)
- Renderer 帧截图能力:第 60 帧自动截 HDRColor/ShadowMap 落 BMP
  (ACES+gamma 还原屏幕观感),视觉调试自闭环不再依赖肉眼
- KeyLight 增加 intensity slider(0-20 运行时可调)
This commit is contained in:
2026-09-24 01:49:11 +08:00
parent 8c0a11d2e8
commit 4d3963e291
8 changed files with 218 additions and 25 deletions
+26 -14
View File
@@ -19,6 +19,7 @@
#include <chrono>
#include <limits>
#include <algorithm>
namespace Engine
{
@@ -173,14 +174,18 @@ namespace Engine
m_Frame.skyInvVP =
glm::inverse(ubo.projNoJitter * glm::mat4(glm::mat3(ubo.view)));
// ---- 2. 灯光(第一个方向光作为 CSM 阴影光源;点光位置来自 Transform)----
u32 lightCount = 0;
// ---- 2. 灯光(CSM 阴影光源约定 = lights[0] 必是方向光)----
// entt Each 遍历序与实体创建序不保证一致——直接按迭代序填充会让
// lights[0] 落到点光上(params.x=1)→ shader 阴影分支整个跳过。
// 先收集到临时数组,方向光排前,再写入 UBO
glm::vec3 shadowDir{0.0f, -1.0f, 0.0f}; // 无灯兜底:头顶正射
LightData collected[kMaxLights];
u32 collectedCount = 0;
scene.Each<TransformComponent, LightComponent>(
[&](Entity, TransformComponent& t, LightComponent& light) {
if (lightCount >= kMaxLights) return;
if (collectedCount >= kMaxLights) return;
auto& dst = ubo.lights[lightCount];
auto& dst = collected[collectedCount++];
dst.color = light.color;
dst.params = glm::vec4(
light.type == LightComponent::Type::Point ? 1.0f : 0.0f,
@@ -195,17 +200,24 @@ namespace Engine
dst.direction = glm::vec4(dir, 0.0f);
dst.position = glm::vec4(0.0f);
}
// CSM 阴影光源 = 第一个方向光(点光不做级联阴影)
if (lightCount == 0 &&
light.type == LightComponent::Type::Directional) {
shadowDir = glm::length(light.direction) > 0.0f
? glm::normalize(light.direction) : shadowDir;
}
++lightCount;
});
for (u32 i = lightCount; i < kMaxLights; ++i) {
ubo.lights[i] = {};
// 方向光排前(稳定排序:同类保持收集序)→ lights[0] = CSM 光源
std::stable_sort(collected, collected + collectedCount,
[](const LightData& a, const LightData& b) {
return a.params.x < b.params.x; // 0=方向光 < 1=点光
});
for (u32 i = 0; i < kMaxLights; ++i) {
if (i < collectedCount) {
ubo.lights[i] = collected[i];
} else {
ubo.lights[i] = {};
}
}
if (collectedCount > 0 &&
collected[0].params.x < 0.5f) {
shadowDir = glm::vec3(collected[0].direction);
}
ComputeCascades(shadowDir);
ubo.cameraPos = glm::vec4(camTransform->GetPosition(), 1.0f);