Files
HelloVulkan/Engine/include/Engine/Renderer/SceneRenderer.h
T
atdunbg 0ed7b4c5b8 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 零警告
2026-09-23 14:59:08 +08:00

94 lines
3.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// Created by atdunbg on 2026/9/23.
//
// SceneRenderer:场景 → 渲染数据的每帧翻译层(UE FSceneRenderer /
// Unity CullingResults 的对应物)。
//
// 职责(全部是"读场景、产数据",不碰 GPU、不碰 Vulkan):
// 1. 相机/灯光/环境色 → UniformBufferObject 语义组装(含阴影光空间矩阵)
// 2. 单趟实例收集 + 双视图剔除(相机视锥/光源视锥各自独立可见列表——
// 相机外实体仍可能投影子进视野,shadow popping 的教训)
// Renderer 拿 FrameSceneData 写 buffer、驱动 pass;场景理解全部收口在此。
//
// 扩展性:
// - 多光源:lights 数组已就位(kMaxLights),每灯各自 lightSpace 即 CSM 前置
// - 级联阴影:BuildFrame 内按级联多出几份 lightSpace + shadowView,
// pass 侧按索引消费,本类结构不变
// - 遮挡剔除/GPU 剔除:只改本类收集逻辑,下游无感知
//
#ifndef HELLOVULKAN_SCENERENDERER_H
#define HELLOVULKAN_SCENERENDERER_H
#include <Engine/Core/Types.h>
#include <Engine/Renderer/SceneTypes.h>
#include <vulkan/vulkan.hpp>
#include <unordered_map>
namespace Engine {
namespace ECS { class Scene; }
namespace Assets { class AssetManager; }
class SceneRenderer {
public:
// 从场景构建当帧渲染数据。无 primary 相机返回 false(渲染跳帧)。
// targetExtent 用于 TAA 子像素抖动的 NDC 偏移折算。
bool BuildFrame(ECS::Scene& scene, Assets::AssetManager& assets,
vk::Extent2D targetExtent);
FrameSceneData& Frame() { return m_Frame; }
// ---- 遥测 ----
u32 GetMainInstanceCount() const { return m_Frame.mainView.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:
// CSM:把相机视锥按实用分割切 N 段,每段独立拟合光源正交投影
//(含纹素对齐防阴影边缘闪烁)。写入(Shadow pass push)与采样
//(Forward UBO)共用同一组矩阵。
void ComputeCascades(const glm::vec3& shadowDir);
FrameSceneData m_Frame;
// ---- 剔除中间量(每帧 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 采样)
glm::mat4 m_LastView{1.0f}; // 上一帧视图(velocity)
glm::mat4 m_LastProjNoJitter{1.0f}; // 上一帧未抖动投影(velocity)
// 实体的上一帧世界矩阵(key = entt 实体 integral)。
// 每帧重建 m_CurWorlds 后与 m_PrevWorlds swap——自动清理销毁实体
std::unordered_map<u32, glm::mat4> m_PrevWorlds;
std::unordered_map<u32, glm::mat4> m_CurWorlds;
bool m_TruncationWarned = false; // 实例缓冲打满只告警一次
};
} // namespace Engine
#endif //HELLOVULKAN_SCENERENDERER_H