EditorCamera (UE FEditorViewportClient semantics) owned by SceneRenderer:
own TransformComponent + reused Camera controller. BuildFrame dispatches
render source by display mode — editor form always renders from the
editor camera (usable in empty scenes), game form renders from the
scene's primary camera entity. AlignTo copies an entity pose into the
editor camera ("Align Viewport to Camera" button in Inspector). F focus
and viewport gizmo now follow the same dispatch. main.cpp stripped of
dangling sandbox camera/spinner state; onSceneLoaded hook removed.
163 lines
7.9 KiB
C++
163 lines
7.9 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/Mesh.h>
|
||
#include <Engine/Renderer/SceneTypes.h>
|
||
#include <glm/gtc/quaternion.hpp>
|
||
#include <vulkan/vulkan.hpp>
|
||
#include <array>
|
||
#include <unordered_map>
|
||
|
||
#include <Engine/Renderer/EditorCamera.h>
|
||
|
||
namespace Engine {
|
||
|
||
namespace ECS { class Scene; }
|
||
namespace Assets { class AssetManager; }
|
||
|
||
class SceneRenderer {
|
||
public:
|
||
// 从场景构建当帧渲染数据。游戏形态无 primary 相机返回 false
|
||
//(渲染跳帧;编辑器形态恒用编辑器相机,不跳)。
|
||
// targetExtent 用于 TAA 子像素抖动的 NDC 偏移折算。
|
||
// dt 驱动骨骼动画采样推进(crossfade 计时)。
|
||
bool BuildFrame(ECS::Scene& scene, Assets::AssetManager& assets,
|
||
vk::Extent2D targetExtent, f32 dt);
|
||
|
||
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; }
|
||
void SetDisplayMode(int m) { m_DisplayMode = m; } // kDisplayDirect / kDisplayViewport
|
||
// 编辑器相机(视口 UI 状态,SceneRenderer 持有 = 渲染源在此分派);
|
||
// Application 驱动 Update,ViewportPanel/Inspector 经 Renderer 门面访问
|
||
EditorCamera& GetEditorCamera() { return m_EditorCamera; }
|
||
// Editor 视口拾取请求(UI 段写入,下一次 BuildFrame 落帧后清除)
|
||
void SetPickRequest(const glm::vec2& ndc)
|
||
{
|
||
m_PendingPick = PickRequest{true, ndc};
|
||
}
|
||
// Editor 选中实体(描边):UI 段写入,BuildFrame 落帧并筛出绘制
|
||
void SetSelectedEntity(u32 entityBits) { m_PendingSelected = entityBits; }
|
||
|
||
private:
|
||
// CSM:把相机视锥按实用分割切 N 段,每段独立拟合光源正交投影
|
||
//(含纹素对齐防阴影边缘闪烁)。写入(Shadow pass push)与采样
|
||
//(Forward UBO)共用同一组矩阵。
|
||
void ComputeCascades(const glm::vec3& shadowDir);
|
||
|
||
// MDI 打包:batch 按组键排序(megabuffer + 可选材质)后压成
|
||
// IndirectDrawCmd 连续数组 + IndirectGroup 区间表。
|
||
// byMaterial=true(Forward):每材质一次 bind + 一次 MDI;
|
||
// byMaterial=false(Shadow):只按 megabuffer 分组(depth-only)
|
||
static void PackIndirect(CullResult& view, bool byMaterial);
|
||
|
||
FrameSceneData m_Frame;
|
||
EditorCamera m_EditorCamera; // 编辑器相机(视口 UI 状态,见 GetEditorCamera)
|
||
PickRequest m_PendingPick{}; // 拾取请求缓冲(BuildFrame 落帧后清除)
|
||
u32 m_PendingSelected = 0; // 选中实体缓冲(描边)
|
||
|
||
// ---- 剔除中间量(每帧 clear 复用,拼接进 m_Frame 后保留容量)----
|
||
struct ViewScratch {
|
||
// cur + prev 合并写:两次随机堆访问 → 一次(cache miss 减半)
|
||
struct InstanceMatrices {
|
||
glm::mat4 cur;
|
||
glm::mat4 prev;
|
||
u32 skinBase = 0; // palette 区段基址(0 = identity 槽)
|
||
u32 entityBits = 0; // 实体位模式(HitProxy id pass 消费)
|
||
};
|
||
CullResult result;
|
||
// 每 batch 自己的实例列表(按 mesh 分组)。GPU 端 batch 语义 =
|
||
// firstInstance + 连续区间,实体序收集不满足(多实体共享 mesh
|
||
// 时实例分散各处),扁平化成连续数组推迟到 appendView
|
||
std::vector<std::vector<InstanceMatrices>> batchMats;
|
||
// 每 batch 实例距离累加(透明排序用:平均距离降序 = 远→近)
|
||
std::vector<f32> batchDistSum;
|
||
// 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();
|
||
batchDistSum.clear();
|
||
for (auto& e : lookup) e.fill(0); // 容量保留,置零复用
|
||
}
|
||
};
|
||
ViewScratch m_MainScratch;
|
||
ViewScratch m_CascScratch[kMaxCascades];
|
||
ViewScratch m_TranslucentScratch; // 透明材质(仅主视图,不投阴影)
|
||
|
||
// ---- 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;
|
||
|
||
// ---- 骨骼动画 scratch(帧首重建/容量复用)----
|
||
// entKey → palette 区段基址(第 2.5 节预采样填,收集段查)
|
||
std::unordered_map<u32, u32> m_EntitySkinBase;
|
||
// 采样层 A(当前 clip)/ B(fade 来源 clip)与 global 累乘
|
||
std::vector<glm::vec3> m_PosePosA, m_PosePosB;
|
||
std::vector<glm::quat> m_PoseRotA, m_PoseRotB;
|
||
std::vector<glm::vec3> m_PoseSclA, m_PoseSclB;
|
||
std::vector<u8> m_TouchedA, m_TouchedB;
|
||
std::vector<glm::mat4> m_Globals;
|
||
bool m_SkinTruncWarned = false; // palette 预算溢出只告警一次
|
||
|
||
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;
|
||
int m_DisplayMode = kDisplayViewport; // Editor 视口为默认形态
|
||
};
|
||
|
||
} // namespace Engine
|
||
|
||
#endif //HELLOVULKAN_SCENERENDERER_H
|