Files
HelloVulkan/Engine/include/Engine/Renderer/SceneRenderer.h
T

120 lines
5.2 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/Mesh.h>
#include <Engine/Renderer/SceneTypes.h>
#include <vulkan/vulkan.hpp>
#include <array>
#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; }
// ---- 运行时开关/调试视图(ImGui 经 Renderer 门面调用)。
// 状态唯一持有者在此,BuildFrame 每帧落入 FrameSceneData;
// TAA 开关同时控制投影抖动,同一持有者天然同步 ----
void SetShadowsEnabled(bool b) { m_ShadowsEnabled = b; }
void SetTAAEnabled(bool b) { m_TAAEnabled = b; }
void SetBloomEnabled(bool b) { m_BloomEnabled = b; }
void SetLodEnabled(bool b) { m_LodEnabled = b; }
void SetDebugView(int v) { m_DebugView = v; }
private:
// CSM:把相机视锥按实用分割切 N 段,每段独立拟合光源正交投影
//(含纹素对齐防阴影边缘闪烁)。写入(Shadow pass push)与采样
//(Forward UBO)共用同一组矩阵。
void ComputeCascades(const glm::vec3& shadowDir);
FrameSceneData m_Frame;
// ---- 剔除中间量(每帧 clear 复用,拼接进 m_Frame 后保留容量)----
struct ViewScratch {
// cur + prev 合并写:两次随机堆访问 → 一次(cache miss 减半)
struct InstanceMatrices {
glm::mat4 cur;
glm::mat4 prev;
};
CullResult result;
// 每 batch 自己的实例列表(按 mesh 分组)。GPU 端 batch 语义 =
// firstInstance + 连续区间,实体序收集不满足(多实体共享 mesh
// 时实例分散各处),扁平化成连续数组推迟到 appendView
std::vector<std::vector<InstanceMatrices>> batchMats;
// batch 查找:数组寻址(下标 = Mesh::GetSlot(),值 = 每 LOD 的
// batchIdx+1,0 = 未用)——逐实例 hash 在大场景是毫秒级开销
std::vector<std::array<u32, kMeshLodCount>> lookup;
void Clear()
{
result.batches.clear();
result.instanceCount = 0;
batchMats.clear();
for (auto& e : lookup) e.fill(0); // 容量保留,置零复用
}
};
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; // 实例缓冲打满只告警一次
// ---- 运行时开关/调试视图状态(本类是唯一写入者)----
bool m_ShadowsEnabled = true;
bool m_TAAEnabled = true;
bool m_BloomEnabled = true;
bool m_LodEnabled = true; // 关 = 全部强制 LOD0(选级跳过)
int m_DebugView = 0;
};
} // namespace Engine
#endif //HELLOVULKAN_SCENERENDERER_H