refactor: 解耦重构——还清四处架构债(对齐 UE/Unity 分层)
- SceneRenderer:场景→渲染数据翻译层(UE FSceneRenderer / Unity CullingResults
模式),收口相机/灯光/光空间矩阵/双视图剔除,Renderer 只编排
- pass 对象化(Unity ScriptableRenderPass 模式):8 个 pass lambda 拆为
Passes::{Shadow,Forward,Bloom,Tonemap,ImGui,Present},BuildGraph 超级函数消失;
SceneGpu/FullscreenGpu 承接共享装配
- 中性 AssetRef(Core 层):ECS 组件不再依赖 Assets 模块(Unity InstanceID 思路)
- IAssetUploader 接口:AssetManager 不感知 Vulkan 具体类,上传服务由渲染层
VulkanAssetUploader 提供
- Renderer 公共 API 不变(App 层零改动),编译零错误 validation 零警告
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/SceneRenderer.h>
|
||||
#include <Engine/ECS/Scene.h>
|
||||
#include <Engine/ECS/Components.h>
|
||||
#include <Engine/Renderer/Camera.h>
|
||||
#include <Engine/Renderer/Frustum.h>
|
||||
#include <Engine/Renderer/Model.h>
|
||||
#include <Engine/Renderer/Mesh.h>
|
||||
#include <Engine/Assets/AssetManager.h>
|
||||
#include <Engine/Core/Log.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace Engine
|
||||
{
|
||||
using namespace ECS; // TransformComponent / Entity 等直接使用
|
||||
|
||||
namespace
|
||||
{
|
||||
// 光源正交包围盒(覆盖当前场景;后续按场景 AABB 自适应)
|
||||
constexpr f32 kLightOrthoExtent = 15.0f;
|
||||
constexpr f32 kLightDistance = 30.0f;
|
||||
constexpr f32 kLightNear = 1.0f;
|
||||
constexpr f32 kLightFar = 80.0f;
|
||||
}
|
||||
|
||||
void SceneRenderer::ComputeLightSpace(const glm::vec3& shadowDir)
|
||||
{
|
||||
// 光源位置在光方向的反方向远处,看向场景中心
|
||||
const glm::vec3 lightPos = -shadowDir * kLightDistance;
|
||||
// up 与视线平行时叉积退化(NaN),切换 up
|
||||
const glm::vec3 up = glm::abs(shadowDir.y) > 0.999f
|
||||
? glm::vec3(0.0f, 0.0f, 1.0f) : glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
const glm::mat4 lightView = glm::lookAt(
|
||||
lightPos, glm::vec3(0.0f), up);
|
||||
|
||||
glm::mat4 lightProj = glm::ortho(
|
||||
-kLightOrthoExtent, kLightOrthoExtent,
|
||||
-kLightOrthoExtent, kLightOrthoExtent,
|
||||
kLightNear, kLightFar);
|
||||
// Vulkan clip 深度 [0,1]:z' = 0.5*z + 0.5*w。
|
||||
// ortho 的 w_clip = 1,偏移折算进 z 的平移分量(与 perspective 公式不同!)
|
||||
lightProj[2][2] *= 0.5f;
|
||||
lightProj[3][2] = lightProj[3][2] * 0.5f + 0.5f;
|
||||
|
||||
m_Frame.ubo.lightSpace = lightProj * lightView;
|
||||
}
|
||||
|
||||
bool SceneRenderer::BuildFrame(ECS::Scene& scene, Assets::AssetManager& assets,
|
||||
f32 aspect)
|
||||
{
|
||||
const auto collectStart = std::chrono::steady_clock::now();
|
||||
|
||||
// ---- 1. 相机 ----
|
||||
auto* cameraComp = scene.GetPrimaryCamera();
|
||||
if (!cameraComp) {
|
||||
// 无相机:本帧跳过(正常场景必有相机,安全网)
|
||||
return false;
|
||||
}
|
||||
const Camera& camera = cameraComp->camera;
|
||||
|
||||
UniformBufferObject& ubo = m_Frame.ubo;
|
||||
ubo.view = camera.GetView();
|
||||
ubo.proj = camera.GetProjection(aspect, 0.1f, 100.0f);
|
||||
ubo.ambientColor = scene.GetSettings().ambientColor;
|
||||
|
||||
// ---- 2. 灯光(第一个灯作为阴影光源)----
|
||||
u32 lightCount = 0;
|
||||
glm::vec3 shadowDir{0.0f, -1.0f, 0.0f}; // 无灯兜底:头顶正射
|
||||
scene.Each<LightComponent>([&](Entity, LightComponent& light) {
|
||||
if (lightCount >= kMaxLights) return;
|
||||
|
||||
glm::vec3 dir = glm::length(light.direction) > 0.0f
|
||||
? glm::normalize(light.direction)
|
||||
: glm::vec3(0.0f);
|
||||
ubo.lights[lightCount].direction = glm::vec4(dir, 0.0f);
|
||||
ubo.lights[lightCount].color = light.color;
|
||||
|
||||
if (lightCount == 0) shadowDir = dir;
|
||||
++lightCount;
|
||||
});
|
||||
for (u32 i = lightCount; i < kMaxLights; ++i) {
|
||||
ubo.lights[i].direction = glm::vec4(0.0f);
|
||||
ubo.lights[i].color = glm::vec4(0.0f);
|
||||
}
|
||||
ComputeLightSpace(shadowDir);
|
||||
|
||||
// ---- 3. GPUDriven:单趟收集 + 双视图剔除 ----
|
||||
// 每个实例对相机视锥、光源视锥分别做包围球测试:
|
||||
// 进相机视锥 → 主视图 batch(Forward 画)
|
||||
// 进光源视锥 → 阴影 batch(Shadow 画)——相机外实体仍可投影子
|
||||
// 实例矩阵数组 = [主视图段 | 阴影视图段],batch.first 为全局下标。
|
||||
// 哈希聚合保证各视图内 batch 区间连续。
|
||||
m_Frame.instanceModels.clear();
|
||||
m_Frame.mainView.batches.clear();
|
||||
m_Frame.shadowView.batches.clear();
|
||||
m_Frame.mainView.instanceCount = 0;
|
||||
m_Frame.shadowView.instanceCount = 0;
|
||||
m_MainLookup.clear();
|
||||
m_ShadowLookup.clear();
|
||||
bool instanceTruncated = false;
|
||||
|
||||
// 剔除平面提取按 [0,1] 深度约定——相机 proj 与 lightSpace 都已
|
||||
// 做 [0,1] 重映射;原始 [-1,1] 矩阵的 near 平面公式不同,勿用
|
||||
const Frustum mainFrustum = Frustum::FromMatrix(ubo.proj * ubo.view);
|
||||
const Frustum shadowFrustum = Frustum::FromMatrix(ubo.lightSpace);
|
||||
|
||||
scene.Each<TransformComponent, MeshRendererComponent>(
|
||||
[&](Entity, TransformComponent& t, MeshRendererComponent& mr) {
|
||||
Model* model = assets.GetModel(mr.model);
|
||||
if (!model) return;
|
||||
|
||||
// 实例世界缩放(矩阵列向量最大长度,包围球半径随缩放)
|
||||
const f32 worldScale = glm::max(
|
||||
glm::length(glm::vec3(t.world[0])),
|
||||
glm::max(glm::length(glm::vec3(t.world[1])),
|
||||
glm::length(glm::vec3(t.world[2]))));
|
||||
|
||||
for (auto& mesh : model->GetMeshes()) {
|
||||
const auto& bounds = mesh->GetBounds();
|
||||
const glm::vec3 worldCenter =
|
||||
glm::vec3(t.world * glm::vec4(bounds.center, 1.0f));
|
||||
const f32 worldRadius = bounds.radius * worldScale;
|
||||
|
||||
auto tryAppend = [&](CullResult& view, const Frustum& frustum,
|
||||
std::unordered_map<Mesh*, u32>& lookup) {
|
||||
if (!frustum.IntersectsSphere(worldCenter, worldRadius)) {
|
||||
return;
|
||||
}
|
||||
if (m_Frame.instanceModels.size() >= kMaxInstances) {
|
||||
instanceTruncated = true;
|
||||
return;
|
||||
}
|
||||
const auto [it, inserted] = lookup.emplace(
|
||||
mesh.get(), static_cast<u32>(view.batches.size()));
|
||||
if (inserted) {
|
||||
view.batches.push_back({mesh.get(),
|
||||
static_cast<u32>(m_Frame.instanceModels.size()), 0});
|
||||
}
|
||||
view.batches[it->second].count++;
|
||||
view.instanceCount++;
|
||||
m_Frame.instanceModels.push_back(t.world);
|
||||
};
|
||||
|
||||
tryAppend(m_Frame.mainView, mainFrustum, m_MainLookup);
|
||||
tryAppend(m_Frame.shadowView, shadowFrustum, m_ShadowLookup);
|
||||
}
|
||||
});
|
||||
|
||||
if (instanceTruncated && !m_TruncationWarned) {
|
||||
// 只告警一次(每帧告警会刷屏)
|
||||
m_TruncationWarned = true;
|
||||
ENGINE_WARN("GPUDriven: instance buffer full ({}), entities truncated",
|
||||
kMaxInstances);
|
||||
}
|
||||
|
||||
m_Frame.collectMs = std::chrono::duration<f32, std::milli>(
|
||||
std::chrono::steady_clock::now() - collectStart).count();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Engine
|
||||
Reference in New Issue
Block a user