- 场景剔除从 [主|阴影] 两视图扩展为 [主|级联0|级联1|级联2] 四视图: 每级联只画自己盒内的 batch,消除原先 3× 全量阴影 draw 的浪费 - 实例数组拼接为四段,batch.first 加段基址保持段内连续; ViewScratch 分桶收集复用容量,稳态零堆分配 - ComputeCascades 沿光方向扩展级联深度范围(pad=30)——切片盒外 caster(高树/城墙)投影进级联的影子不会被剔除裁掉,per-cascade 剔除正确性的前提;只扩 z 不缩 XY,分辨率不受影响 - 遥测 shadow 计数 = 各级联求和 - SunTemple 1056 mesh 全量加载,validation 零警告
94 lines
3.6 KiB
C++
94 lines
3.6 KiB
C++
//
|
||
// 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
|