perf: CSM 每级联独立剔除 + 光向 caster pad

- 场景剔除从 [主|阴影] 两视图扩展为 [主|级联0|级联1|级联2] 四视图:
  每级联只画自己盒内的 batch,消除原先 3× 全量阴影 draw 的浪费
- 实例数组拼接为四段,batch.first 加段基址保持段内连续;
  ViewScratch 分桶收集复用容量,稳态零堆分配
- ComputeCascades 沿光方向扩展级联深度范围(pad=30)——切片盒外
  caster(高树/城墙)投影进级联的影子不会被剔除裁掉,per-cascade
  剔除正确性的前提;只扩 z 不缩 XY,分辨率不受影响
- 遥测 shadow 计数 = 各级联求和
- SunTemple 1056 mesh 全量加载,validation 零警告
This commit is contained in:
2026-09-23 14:59:08 +08:00
parent c54f60c5ea
commit 0ed7b4c5b8
4 changed files with 87 additions and 36 deletions
+24 -4
View File
@@ -41,7 +41,12 @@ namespace Engine {
// ---- 遥测 ----
u32 GetMainInstanceCount() const { return m_Frame.mainView.instanceCount; }
u32 GetShadowInstanceCount() const { return m_Frame.shadowView.instanceCount; }
u32 GetShadowInstanceCount() const
{
u32 sum = 0;
for (const auto& c : m_Frame.shadowCascades) sum += c.instanceCount;
return sum;
}
f32 GetCollectMs() const { return m_Frame.collectMs; }
private:
@@ -52,9 +57,24 @@ namespace Engine {
FrameSceneData m_Frame;
// batch 聚合查找表(mesh → batch 下标),每帧 clear 复用桶
std::unordered_map<Mesh*, u32> m_MainLookup;
std::unordered_map<Mesh*, u32> m_ShadowLookup;
// ---- 剔除中间量(每帧 clear 复用,拼接进 m_Frame 后保留容量)----
struct ViewScratch {
CullResult result;
std::vector<glm::mat4> worlds;
std::vector<glm::mat4> prevWorlds;
std::unordered_map<Mesh*, u32> lookup;
void Clear()
{
result.batches.clear();
result.instanceCount = 0;
worlds.clear();
prevWorlds.clear();
lookup.clear();
}
};
ViewScratch m_MainScratch;
ViewScratch m_CascScratch[kMaxCascades];
// ---- TAA 时域状态 ----
u64 m_FrameIndex = 0; // 抖动序列游标(Halton 2,3 ×8 采样)
+3 -1
View File
@@ -71,7 +71,9 @@ namespace Engine {
// 实例的上一帧姿态,TAA velocity 用);首帧/新实体 = 当前矩阵
std::vector<glm::mat4> prevInstanceModels;
CullResult mainView; // 相机视锥 → Forward pass
CullResult shadowView; // 光源视锥 → Shadow pass(相机外实体仍投影子)
// 每级联独立剔除结果(Shadow pass 按 region 各画各的——
// 级联盒沿光方向加了 caster pad,影子不会被剔除裁掉)
CullResult shadowCascades[kMaxCascades];
f32 collectMs = 0.0f; // 收集+剔除 CPU 耗时(遥测)
};