From 2c4efc516521963c51a2ab692172f0412bba8a15 Mon Sep 17 00:00:00 2001 From: atdunbg Date: Wed, 23 Sep 2026 17:46:25 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A4=9A=E5=85=89=E6=BA=90=E2=80=94?= =?UTF-8?q?=E2=80=94=E7=82=B9=E5=85=89=E6=BA=90=E7=B1=BB=E5=9E=8B=EF=BC=88?= =?UTF-8?q?=E8=B7=9D=E7=A6=BB=E8=A1=B0=E5=87=8F=EF=BC=8C=E4=BD=8D=E7=BD=AE?= =?UTF-8?q?=E6=9D=A5=E8=87=AA=20Transform=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - LightComponent 加 Type(Directional/Point)+ radius;点光位置取 实体 Transform(固有组件首次实际受益) - LightData shader 契约扩为 4×vec4(color/direction/position/params, 每灯 64B,未用字段置零);UBO 688→816B - 光照循环按类型分支:方向光无衰减;点光 L 指向光源 + 平方距离衰减, 衰减半径外跳过;CSM 阴影只来自第 0 盏方向光 - main.cpp:SunTemple 场景加暖橙火坑光 + 冷蓝填充光(点光); KeyLight/FillLight 改 designated initializer --- App/main.cpp | 32 ++++++++++++++--- Engine/include/Engine/Core/Application.h | 2 +- Engine/include/Engine/ECS/Components.h | 12 +++++-- Engine/include/Engine/Renderer/SceneTypes.h | 9 +++-- Engine/include/Engine/Vulkan/Buffer.h | 2 +- .../include/Engine/Vulkan/MemoryAllocator.h | 8 ++--- Engine/src/Core/Application.cpp | 4 +-- Engine/src/Renderer/SceneRenderer.cpp | 35 +++++++++++++------ Engine/src/Vulkan/MemoryAllocator.cpp | 8 ++--- shaders/simple_shader.frag | 30 +++++++++++++--- shaders/simple_shader.vert | 6 ++-- 11 files changed, 108 insertions(+), 40 deletions(-) diff --git a/App/main.cpp b/App/main.cpp index a526fa4..1f04c9e 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -77,20 +77,41 @@ protected: e.AddComponent(MeshRendererComponent{temple}); } - // 灯光实体(第一个灯 = 阴影光源) + // 灯光实体(第一个方向光 = 阴影光源) m_KeyLight = scene.CreateEntity("KeyLight"); m_KeyLight.AddComponent( LightComponent{ - glm::vec3(-0.5f, -0.6f, -0.35f), - glm::vec4(1.0f, 0.95f, 0.9f, 1.0f) + .type = LightComponent::Type::Directional, + .direction = glm::vec3(-0.5f, -0.6f, -0.35f), + .color = glm::vec4(1.0f, 0.95f, 0.9f, 1.0f) }); scene.CreateEntity("FillLight") .AddComponent( LightComponent{ - glm::vec3(0.5f, -0.5f, 0.3f), - glm::vec4(0.3f, 0.5f, 1.0f, 0.8f) + .type = LightComponent::Type::Directional, + .direction = glm::vec3(0.5f, -0.5f, 0.3f), + .color = glm::vec4(0.3f, 0.5f, 1.0f, 0.8f) }); + + // 点光源(位置 = 实体 Transform,出生即有;无阴影,距离衰减) + auto fire = scene.CreateEntity("FirePitLight"); + fire.GetComponent().SetPosition(glm::vec3(2.0f, 1.5f, 1.0f)); + fire.AddComponent( + LightComponent{ + .type = LightComponent::Type::Point, + .color = glm::vec4(1.0f, 0.6f, 0.3f, 2.0f), // 暖橙 + .radius = 8.0f, + }); + + auto cool = scene.CreateEntity("CoolFillLight"); + cool.GetComponent().SetPosition(glm::vec3(-2.5f, 2.0f, -2.0f)); + cool.AddComponent( + LightComponent{ + .type = LightComponent::Type::Point, + .color = glm::vec4(0.3f, 0.5f, 1.0f, 1.2f), // 冷蓝 + .radius = 10.0f, + }); } void OnUpdate(f32 deltaTime) override @@ -258,6 +279,7 @@ protected: void OnWindowClose(const Event& event) override { + SetImGuiEnabled(true); m_ShowQuitConfirm = true; } diff --git a/Engine/include/Engine/Core/Application.h b/Engine/include/Engine/Core/Application.h index 0a2462e..65f3b04 100644 --- a/Engine/include/Engine/Core/Application.h +++ b/Engine/include/Engine/Core/Application.h @@ -39,7 +39,7 @@ namespace Engine { class Application { public: - Application(std::string title, u32 width, u32 height); + Application(const std::string& title, u32 width, u32 height); virtual ~Application(); Application(const Application&) = delete; diff --git a/Engine/include/Engine/ECS/Components.h b/Engine/include/Engine/ECS/Components.h index 9d71a8d..a7f3864 100644 --- a/Engine/include/Engine/ECS/Components.h +++ b/Engine/include/Engine/ECS/Components.h @@ -89,10 +89,16 @@ namespace Engine::ECS { bool primary = false; }; - // 方向光 + // 灯光。类型: + // Directional——方向即照射方向(无位置,阴影光源用第 0 盏方向光) + // Point——位置取实体 Transform(固有的),向四周发光 + 距离衰减 struct LightComponent { - glm::vec3 direction{0.0f, -1.0f, 0.0f}; - glm::vec4 color{1.0f, 1.0f, 1.0f, 1.0f}; + enum class Type : u8 { Directional = 0, Point = 1 }; + + Type type = Type::Directional; + glm::vec3 direction{0.0f, -1.0f, 0.0f}; // 仅方向光 + glm::vec4 color{1.0f, 1.0f, 1.0f, 1.0f}; // rgb = 颜色,a = 强度 + f32 radius = 10.0f; // 仅点光:衰减半径 }; } // namespace Engine::ECS diff --git a/Engine/include/Engine/Renderer/SceneTypes.h b/Engine/include/Engine/Renderer/SceneTypes.h index 12f4c6c..d8b0f00 100644 --- a/Engine/include/Engine/Renderer/SceneTypes.h +++ b/Engine/include/Engine/Renderer/SceneTypes.h @@ -37,10 +37,13 @@ namespace Engine { constexpr u32 kMaxCascades = 3; // CSM 级联数 constexpr u32 kCascadeRes = 1024; // 每级联阴影图边长 - // 镜像 shader 侧 UBO 布局的灯光项 + // 镜像 shader 侧 UBO 布局的灯光项(std140,每灯 64B)。 + // 字段未用部分置零——shader 按类型分支读取 struct LightData { - glm::vec4 direction; // xyz = 方向(已归一),w 未用 - glm::vec4 color; // rgb = 颜色,a = 强度 + glm::vec4 color; // rgb = 颜色,a = 强度 + glm::vec4 direction; // 方向光:xyz = 方向(已归一);点光未用 + glm::vec4 position; // 点光:xyz = 世界位置(来自实体 Transform) + glm::vec4 params; // x = 类型(0=方向,1=点),y = 衰减半径,z/w 未用 }; // 镜像 simple_shader 的 UBO 布局(std140 语义由 shader 侧对齐)。 diff --git a/Engine/include/Engine/Vulkan/Buffer.h b/Engine/include/Engine/Vulkan/Buffer.h index c26f4e8..24ef335 100644 --- a/Engine/include/Engine/Vulkan/Buffer.h +++ b/Engine/include/Engine/Vulkan/Buffer.h @@ -29,7 +29,7 @@ namespace Engine { vk::DeviceSize GetSize() const { return m_Size; } // 只有 HOST_VISIBLE 的 Buffer 能调 - void Upload(const void* data, vk::DeviceSize size, vk::DeviceSize offset = 0); + void Upload(const void* data, vk::DeviceSize size, vk::DeviceSize offset = 0) const; private: Device& m_Device; diff --git a/Engine/include/Engine/Vulkan/MemoryAllocator.h b/Engine/include/Engine/Vulkan/MemoryAllocator.h index 6eaf70f..da889dd 100644 --- a/Engine/include/Engine/Vulkan/MemoryAllocator.h +++ b/Engine/include/Engine/Vulkan/MemoryAllocator.h @@ -54,15 +54,15 @@ namespace Engine { // 创建 + 分配 + 绑定一步完成(VMA 惯用法) Allocation CreateBuffer(vk::DeviceSize size, vk::BufferUsageFlags usage, - vk::MemoryPropertyFlags props); + vk::MemoryPropertyFlags props) const; Allocation CreateImage(const vk::ImageCreateInfo& imageInfo, - vk::MemoryPropertyFlags props); + vk::MemoryPropertyFlags props) const; // 销毁资源并释放分配(按 alloc.Buffer/Image 判断类型),随后 alloc 归零 - void Destroy(Allocation& alloc); + void Destroy(Allocation& alloc) const; // HOST_VISIBLE 非 coherent 写入后冲刷(VMA 内部对 coherent 自动跳过) - void Flush(const Allocation& alloc); + void Flush(const Allocation& alloc) const; void PrintStats() const; diff --git a/Engine/src/Core/Application.cpp b/Engine/src/Core/Application.cpp index 34ae06f..8fbe455 100644 --- a/Engine/src/Core/Application.cpp +++ b/Engine/src/Core/Application.cpp @@ -20,9 +20,9 @@ namespace Engine { - Application::Application(std::string title, u32 width, u32 height) + Application::Application(const std::string& title, u32 width, u32 height) { - m_Window = CreateScope(std::move(title), width, height); + m_Window = CreateScope(title, width, height); // 事件通道:GLFW 回调 → 本类 DispatchEvent → 派生类钩子 m_Window->SetEventCallback( diff --git a/Engine/src/Renderer/SceneRenderer.cpp b/Engine/src/Renderer/SceneRenderer.cpp index 799d27a..e3026e2 100644 --- a/Engine/src/Renderer/SceneRenderer.cpp +++ b/Engine/src/Renderer/SceneRenderer.cpp @@ -168,24 +168,39 @@ namespace Engine ubo.ambientColor = scene.GetSettings().ambientColor; - // ---- 2. 灯光(第一个灯作为阴影光源)---- + // ---- 2. 灯光(第一个方向光作为 CSM 阴影光源;点光位置来自 Transform)---- u32 lightCount = 0; glm::vec3 shadowDir{0.0f, -1.0f, 0.0f}; // 无灯兜底:头顶正射 - scene.Each([&](Entity, LightComponent& light) { + scene.Each( + [&](Entity, TransformComponent& t, 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; + auto& dst = ubo.lights[lightCount]; + dst.color = light.color; + dst.params = glm::vec4( + light.type == LightComponent::Type::Point ? 1.0f : 0.0f, + light.radius, 0.0f, 0.0f); - if (lightCount == 0) shadowDir = dir; + if (light.type == LightComponent::Type::Point) { + dst.position = glm::vec4(t.GetPosition(), 1.0f); + dst.direction = glm::vec4(0.0f); + } else { + glm::vec3 dir = glm::length(light.direction) > 0.0f + ? glm::normalize(light.direction) : glm::vec3(0.0f); + dst.direction = glm::vec4(dir, 0.0f); + dst.position = glm::vec4(0.0f); + } + + // CSM 阴影光源 = 第一个方向光(点光不做级联阴影) + if (lightCount == 0 && + light.type == LightComponent::Type::Directional) { + shadowDir = glm::length(light.direction) > 0.0f + ? glm::normalize(light.direction) : shadowDir; + } ++lightCount; }); for (u32 i = lightCount; i < kMaxLights; ++i) { - ubo.lights[i].direction = glm::vec4(0.0f); - ubo.lights[i].color = glm::vec4(0.0f); + ubo.lights[i] = {}; } ComputeCascades(shadowDir); ubo.cameraPos = glm::vec4(camTransform->GetPosition(), 1.0f); diff --git a/Engine/src/Vulkan/MemoryAllocator.cpp b/Engine/src/Vulkan/MemoryAllocator.cpp index 02f13a7..e899bc3 100644 --- a/Engine/src/Vulkan/MemoryAllocator.cpp +++ b/Engine/src/Vulkan/MemoryAllocator.cpp @@ -61,7 +61,7 @@ namespace Engine { } MemoryAllocator::Allocation MemoryAllocator::CreateBuffer( - vk::DeviceSize size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags props) + vk::DeviceSize size, const vk::BufferUsageFlags usage, const vk::MemoryPropertyFlags props) const { VkBufferCreateInfo bi{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO}; bi.size = size; @@ -92,7 +92,7 @@ namespace Engine { } MemoryAllocator::Allocation MemoryAllocator::CreateImage( - const vk::ImageCreateInfo& imageInfo, vk::MemoryPropertyFlags props) + const vk::ImageCreateInfo& imageInfo, const vk::MemoryPropertyFlags props) const { VkImageCreateInfo ii = imageInfo; // 同布局,隐式转换 @@ -116,7 +116,7 @@ namespace Engine { return out; } - void MemoryAllocator::Destroy(Allocation& alloc) + void MemoryAllocator::Destroy(Allocation& alloc) const { if (!alloc.Valid) return; @@ -131,7 +131,7 @@ namespace Engine { alloc = {}; } - void MemoryAllocator::Flush(const Allocation& alloc) + void MemoryAllocator::Flush(const Allocation& alloc) const { if (!alloc.Valid || !alloc.Handle) return; // HOST_COHERENT 时 VMA 内部自动跳过 diff --git a/shaders/simple_shader.frag b/shaders/simple_shader.frag index 6f58080..f45fd24 100644 --- a/shaders/simple_shader.frag +++ b/shaders/simple_shader.frag @@ -3,8 +3,10 @@ const int MAX_LIGHTS = 4; struct Light { - vec4 direction; - vec4 color; + vec4 color; // rgb = 颜色,a = 强度 + vec4 direction; // 方向光:xyz = 方向(已归一) + vec4 position; // 点光:xyz = 世界位置 + vec4 params; // x = 类型(0=方向,1=点),y = 衰减半径 }; layout(location = 0) in vec2 fragUV; @@ -92,15 +94,33 @@ void main() { vec3 result = ubo.ambientColor.rgb * albedo; for (int i = 0; i < MAX_LIGHTS; ++i) { - vec3 L = normalize(-ubo.lights[i].direction.xyz); + // 按类型分支:方向光无衰减;点光方向指向光源 + 平方距离衰减 + vec3 L; + float atten = 1.0; + if (ubo.lights[i].params.x > 0.5) { + vec3 toLight = ubo.lights[i].position.xyz - fragWorldPos; + float d = length(toLight); + if (d >= ubo.lights[i].params.y) continue; // 衰减半径外,跳过 + L = toLight / d; + float a = clamp(1.0 - (d * d) / + (ubo.lights[i].params.y * ubo.lights[i].params.y), + 0.0, 1.0); + atten = a * a; // 平方衰减,边缘更柔和 + } else { + L = normalize(-ubo.lights[i].direction.xyz); + } + float diff = max(dot(N, L), 0.0); // Blinn-Phong 高光(半程向量;specular 图做强度系数) vec3 H = normalize(L + V); float spec = pow(max(dot(N, H), 0.0), 32.0) * specIntensity; - float shade = (i == 0) ? ShadowFactor(N, fragViewDepth) : 1.0; + // 阴影只来自第 0 盏方向光(CSM) + float shade = (i == 0 && ubo.lights[0].params.x < 0.5) + ? ShadowFactor(N, fragViewDepth) : 1.0; + result += ubo.lights[i].color.rgb * ubo.lights[i].color.a * - (albedo * diff + vec3(spec)) * shade; + (albedo * diff + vec3(spec)) * atten * shade; } // 自发光不经光照/阴影直接叠加 diff --git a/shaders/simple_shader.vert b/shaders/simple_shader.vert index c12787a..ac5db07 100644 --- a/shaders/simple_shader.vert +++ b/shaders/simple_shader.vert @@ -3,8 +3,10 @@ const int MAX_LIGHTS = 4; struct Light { - vec4 direction; - vec4 color; + vec4 color; // rgb = 颜色,a = 强度 + vec4 direction; // 方向光:xyz = 方向(已归一) + vec4 position; // 点光:xyz = 世界位置 + vec4 params; // x = 类型(0=方向,1=点),y = 衰减半径 }; layout(location = 0) in vec3 inPosition;