From 7d137fa9b1d48c027e2ab2cf2bba6c1d6ec640e9 Mon Sep 17 00:00:00 2001 From: atdunbg Date: Wed, 23 Sep 2026 15:35:20 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20Transform=20=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E5=B0=81=E8=A3=85=20+=20dirty=20=E6=A0=87=E8=AE=B0=EF=BC=88?= =?UTF-8?q?=E5=AF=B9=E9=BD=90=20UE=20USceneComponent=20=E7=9C=9F=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修正上一版"全量暴力重算":UE 实为 dirty 标记 + 惰性计算,前提是 字段私有(SetRelativeLocation 等 setter 标脏 + 子级传播) - TransformComponent:SRT 字段私有化,Set/Get/World()/IsDirty 访问器; Set* 变更才标脏;AddRotationY 便捷接口 - Scene::UpdateTransforms 只重算脏实体——静止实体零成本; 层级(父子)传播在脏标记基础上扩展 - 迁移 Camera/SceneRenderer/main.cpp 到封装 API --- App/main.cpp | 4 +- Engine/include/Engine/ECS/Components.h | 53 +++++++++++++++++--------- Engine/src/ECS/Scene.cpp | 7 ++-- Engine/src/Renderer/Camera.cpp | 30 ++++++++------- Engine/src/Renderer/SceneRenderer.cpp | 16 ++++---- 5 files changed, 63 insertions(+), 47 deletions(-) diff --git a/App/main.cpp b/App/main.cpp index 03eb88e..ab8c405 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -112,8 +112,8 @@ protected: for (size_t i = 0; i < m_Spinners.size(); ++i) { auto& t = m_Spinners[i].GetComponent(); - t.rotation.y = m_Time * 60.0f + static_cast(i) * 40.0f; - t.scale = glm::vec3(0.5f); // dirty → 帧循环统一 Compose + t.SetRotation(glm::vec3(0.0f, m_Time * 60.0f + static_cast(i) * 40.0f, 0.0f)); + t.SetScale(glm::vec3(0.5f)); } } diff --git a/Engine/include/Engine/ECS/Components.h b/Engine/include/Engine/ECS/Components.h index 54b0495..64e5bc1 100644 --- a/Engine/include/Engine/ECS/Components.h +++ b/Engine/include/Engine/ECS/Components.h @@ -34,34 +34,49 @@ namespace Engine::ECS { std::string name; }; - // 世界变换:可编辑的 SRT 分量 + 派生的 world 矩阵。 - // 分量可直接读写(含直接改字段);world 由帧循环的 - // Scene::UpdateTransforms() 全量重算(UE transform-update 阶段 - // 模式),调用方永远不需要手动 Compose。渲染热路径只读 world。 + // 世界变换:UE USceneComponent 的 encapsulation 模式—— + // SRT 分量私有,所有写入口标脏;world 矩阵由帧循环的 + // Scene::UpdateTransforms() 只对脏的实体重算(transform-update + // 阶段),并自动向未来的子层级传播。渲染热路径只读 World()。 // 层级(父子变换)留给后续的层级同步系统。 struct TransformComponent { - glm::vec3 position{0.0f}; - glm::vec3 rotation{0.0f}; // 欧拉角(度),应用顺序 Y→X→Z - glm::vec3 scale{1.0f}; - glm::mat4 world{1.0f}; + // ---- 读取 ---- + const glm::vec3& GetPosition() const { return m_position; } + const glm::vec3& GetRotation() const { return m_rotation; } // 欧拉角(度) + const glm::vec3& GetScale() const { return m_scale; } + const glm::mat4& World() const { return m_world; } // 要求已由 UpdateTransforms 计算 - TransformComponent& SetPosition(const glm::vec3& p) { position = p; return *this; } - TransformComponent& SetRotation(const glm::vec3& r) { rotation = r; return *this; } - TransformComponent& SetScale(const glm::vec3& s) { scale = s; return *this; } + // ---- 写入(标脏,脏实体由 UpdateTransforms 重算)---- + TransformComponent& SetPosition(const glm::vec3& p) { if (m_position != p) { m_position = p; m_dirty = true; } return *this; } + TransformComponent& SetRotation(const glm::vec3& r) { if (m_rotation != r) { m_rotation = r; m_dirty = true; } return *this; } + TransformComponent& SetScale(const glm::vec3& s) { if (m_scale != s) { m_scale = s; m_dirty = true; } return *this; } - // SRT 分量 → world 矩阵(T * R * S)。 - // 由 UpdateTransforms 每帧全量调用;勿在用户代码手动调用 + // 便捷:原地修改单个欧拉角分量(度) + TransformComponent& AddRotationY(f32 degY) { m_rotation.y += degY; m_dirty = true; return *this; } + + bool IsDirty() const { return m_dirty; } + + // SRT 分量 → world 矩阵(T * R * S),清除脏标记。 + // 由 Scene::UpdateTransforms 调用(或层级系统),勿手动 TransformComponent& Compose() { - const glm::mat4 t = glm::translate(glm::mat4(1.0f), position); + const glm::mat4 t = glm::translate(glm::mat4(1.0f), m_position); const glm::mat4 r = - glm::rotate(glm::mat4(1.0f), glm::radians(rotation.y), glm::vec3(0, 1, 0)) * - glm::rotate(glm::mat4(1.0f), glm::radians(rotation.x), glm::vec3(1, 0, 0)) * - glm::rotate(glm::mat4(1.0f), glm::radians(rotation.z), glm::vec3(0, 0, 1)); - const glm::mat4 s = glm::scale(glm::mat4(1.0f), scale); - world = t * r * s; + glm::rotate(glm::mat4(1.0f), glm::radians(m_rotation.y), glm::vec3(0, 1, 0)) * + glm::rotate(glm::mat4(1.0f), glm::radians(m_rotation.x), glm::vec3(1, 0, 0)) * + glm::rotate(glm::mat4(1.0f), glm::radians(m_rotation.z), glm::vec3(0, 0, 1)); + const glm::mat4 s = glm::scale(glm::mat4(1.0f), m_scale); + m_world = t * r * s; + m_dirty = false; return *this; } + + private: + glm::vec3 m_position{0.0f}; + glm::vec3 m_rotation{0.0f}; + glm::vec3 m_scale{1.0f}; + glm::mat4 m_world{1.0f}; + bool m_dirty = true; // 出生即脏:首次 UpdateTransforms 计算 world }; // 网格渲染体:引用共享的 Model 资产。 diff --git a/Engine/src/ECS/Scene.cpp b/Engine/src/ECS/Scene.cpp index f1fdbe2..0ec0f4b 100644 --- a/Engine/src/ECS/Scene.cpp +++ b/Engine/src/ECS/Scene.cpp @@ -55,11 +55,10 @@ namespace Engine::ECS { void Scene::UpdateTransforms() { - // 全量重算:分量允许直接写字段(无法可靠 dirty 追踪), - // 无条件 Compose 保证任何写法下 world 都新鲜。 - // 实测万级实体内成本可忽略;更大规模再引入 dirty+封装优化 + // 只重算脏的:UE transform-update 阶段模式。 + // 静止实体(绝大多数)零成本;层级传播在此扩展 m_Registry.view().each([](TransformComponent& t) { - t.Compose(); + if (t.IsDirty()) t.Compose(); }); } diff --git a/Engine/src/Renderer/Camera.cpp b/Engine/src/Renderer/Camera.cpp index f5f8fce..621e823 100644 --- a/Engine/src/Renderer/Camera.cpp +++ b/Engine/src/Renderer/Camera.cpp @@ -14,8 +14,7 @@ namespace Engine { void Camera::Update(Window& window, ECS::TransformComponent& t, f32 deltaTime) { - // 鼠标旋转:直接写实体旋转(yaw → rotation.y,pitch → rotation.x)。 - // Set*/成员写后由帧循环的 UpdateTransforms 统一 Compose + // 鼠标旋转:直接写实体旋转(yaw → rotation.y,pitch → rotation.x) if (m_MouseLookEnabled) { f32 mouseDX = 0, mouseDY = 0; @@ -23,35 +22,38 @@ namespace Engine { if (mouseDX != 0 || mouseDY != 0) { - t.rotation.y += mouseDX * m_MouseSensitivity; - t.rotation.x = glm::clamp( - t.rotation.x - mouseDY * m_MouseSensitivity, -89.0f, 89.0f); + glm::vec3 rot = t.GetRotation(); + rot.y += mouseDX * m_MouseSensitivity; + rot.x = glm::clamp( + rot.x - mouseDY * m_MouseSensitivity, -89.0f, 89.0f); + t.SetRotation(rot); } } // 基向量由实体旋转决定(本地 -Z 为视线方向;应用顺序 Y→X, // 与 TransformComponent::Compose 一致;roll 不参与 FPS 相机) + const glm::vec3& rot = t.GetRotation(); const glm::mat3 r = static_cast( - glm::rotate(glm::mat4(1.0f), glm::radians(t.rotation.y), + glm::rotate(glm::mat4(1.0f), glm::radians(rot.y), glm::vec3(0, 1, 0)) * - glm::rotate(glm::mat4(1.0f), glm::radians(t.rotation.x), + glm::rotate(glm::mat4(1.0f), glm::radians(rot.x), glm::vec3(1, 0, 0))); const glm::vec3 forward = r * glm::vec3(0, 0, -1); const glm::vec3 right = r * glm::vec3(1, 0, 0); f32 speed = m_MoveSpeed * deltaTime; - if (window.IsKeyPressed(GLFW_KEY_W)) { t.position += forward * speed; } - if (window.IsKeyPressed(GLFW_KEY_S)) { t.position -= forward * speed; } - if (window.IsKeyPressed(GLFW_KEY_A)) { t.position -= right * speed; } - if (window.IsKeyPressed(GLFW_KEY_D)) { t.position += right * speed; } - if (window.IsKeyPressed(GLFW_KEY_SPACE)) { t.position.y += speed; } - if (window.IsKeyPressed(GLFW_KEY_LEFT_SHIFT)) { t.position.y -= speed; } + if (window.IsKeyPressed(GLFW_KEY_W)) { t.SetPosition(t.GetPosition() + forward * speed); } + if (window.IsKeyPressed(GLFW_KEY_S)) { t.SetPosition(t.GetPosition() - forward * speed); } + if (window.IsKeyPressed(GLFW_KEY_A)) { t.SetPosition(t.GetPosition() - right * speed); } + if (window.IsKeyPressed(GLFW_KEY_D)) { t.SetPosition(t.GetPosition() + right * speed); } + if (window.IsKeyPressed(GLFW_KEY_SPACE)) { t.SetPosition(t.GetPosition() + glm::vec3(0, speed, 0)); } + if (window.IsKeyPressed(GLFW_KEY_LEFT_SHIFT)) { t.SetPosition(t.GetPosition() - glm::vec3(0, speed, 0)); } } glm::mat4 Camera::GetView(const ECS::TransformComponent& t) const { // 相机作为实体:视图 = 世界矩阵的逆(无重复位姿状态) - return glm::inverse(t.world); + return glm::inverse(t.World()); } glm::mat4 Camera::GetProjection(f32 aspect, f32 nearZ, f32 farZ) const diff --git a/Engine/src/Renderer/SceneRenderer.cpp b/Engine/src/Renderer/SceneRenderer.cpp index 5a4c439..41e29f0 100644 --- a/Engine/src/Renderer/SceneRenderer.cpp +++ b/Engine/src/Renderer/SceneRenderer.cpp @@ -185,7 +185,7 @@ namespace Engine ubo.lights[i].color = glm::vec4(0.0f); } ComputeCascades(shadowDir); - ubo.cameraPos = glm::vec4(camTransform->position, 1.0f); + ubo.cameraPos = glm::vec4(camTransform->GetPosition(), 1.0f); // ---- 3. GPUDriven:单趟收集 + 多视图剔除 ---- // 每个实例的包围球对 4 个视锥分别测试: @@ -218,22 +218,22 @@ namespace Engine // TAA velocity:本实例的上一帧世界矩阵(首帧/新实体 = 当前 // 矩阵 → 速度为 0,不产生假运动) const u32 entKey = static_cast(entt::to_integral(entity)); - glm::mat4 prevWorld = t.world; + glm::mat4 prevWorld = t.World(); if (auto it = m_PrevWorlds.find(entKey); it != m_PrevWorlds.end()) { prevWorld = it->second; } - m_CurWorlds.emplace(entKey, t.world); + m_CurWorlds.emplace(entKey, t.World()); // 实例世界缩放(矩阵列向量最大长度,包围球半径随缩放) 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])))); + 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)); + glm::vec3(t.World() * glm::vec4(bounds.center, 1.0f)); const f32 worldRadius = bounds.radius * worldScale; auto tryAppend = [&](ViewScratch& s, const Frustum& frustum) { @@ -252,7 +252,7 @@ namespace Engine } s.result.batches[it->second].count++; s.result.instanceCount++; - s.worlds.push_back(t.world); + s.worlds.push_back(t.World()); s.prevWorlds.push_back(prevWorld); ++totalInstances; };