From b86ca460decabdd2a245ddf0d57b4d8e6de13f8b Mon Sep 17 00:00:00 2001 From: atdunbg Date: Tue, 22 Sep 2026 15:08:08 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=98=B4=E5=BD=B1=E5=85=89=E6=BA=90?= =?UTF-8?q?=E4=BD=8D=E7=BD=AE=E8=AE=A1=E7=AE=97=E5=8F=8D=E5=90=91=20+=20lo?= =?UTF-8?q?okAt=20=E5=9E=82=E7=9B=B4=E5=85=89=E9=80=80=E5=8C=96=E2=80=94?= =?UTF-8?q?=E2=80=94=E5=BD=B1=E5=AD=90=E5=8F=AF=E8=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因1(致命):光源位置写成 lightDir*30。lightDir 是光的传播方向 (指向场景),乘 30 后光源跑到场景下方——从地面下方向上打光, 地面永远是光线第一个交点,物体永远不可能遮挡地面,影子恒不存在。 修复:光源位置 = -lightDir * 30(逆传播方向退回)。 根因2:垂直光(direction≈(0,-1,0))时 lookAt 的 up=(0,1,0) 与视线 平行,叉积为零 → 矩阵 NaN → shadow map 全垃圾。修复:|dir.y|>0.99 时改用 (0,0,1) 作为 up。 场景:地面 quad(Model::CreatePlane 程序化构造,AssetManager 缓存) + 主光调平让影子落在相机视野内。已截图验证影子可见。 --- App/main.cpp | 11 ++++-- Engine/include/Engine/Assets/AssetManager.h | 3 ++ Engine/include/Engine/Renderer/Model.h | 10 ++++++ Engine/src/Assets/AssetManager.cpp | 10 ++++++ Engine/src/Renderer/Model.cpp | 38 +++++++++++++++++++++ Engine/src/Renderer/Renderer.cpp | 15 ++++++-- 6 files changed, 82 insertions(+), 5 deletions(-) diff --git a/App/main.cpp b/App/main.cpp index 70d0c12..8ce9921 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -42,6 +42,13 @@ protected: // 模型资产(AssetManager 负责加载/缓存/生命周期,返回句柄) auto backpack = GetAssets().LoadModel("assets/models/backpack/backpack.obj"); + // 地面:接收阴影 + auto ground = GetAssets().CreatePlane(30.0f, "assets/textures/default.tga"); + auto groundEntity = scene.CreateEntity("Ground"); + groundEntity.AddComponent().world = + glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, -2.0f, 0.0f)); + groundEntity.AddComponent(MeshRendererComponent{ground}); + // 相机实体(primary = 当前渲染视角) m_Camera = scene.CreateEntity("MainCamera"); auto& camera = m_Camera.AddComponent(); @@ -61,10 +68,10 @@ protected: m_Spinners.push_back(e); } - // 灯光实体 + // 灯光实体(主光较平,影子拉长可见) scene.CreateEntity("KeyLight") .AddComponent( - LightComponent{glm::vec3(-0.5f, -1.0f, -0.3f), + LightComponent{glm::vec3(-0.5f, -0.6f, -0.35f), glm::vec4(1.0f, 0.95f, 0.9f, 1.0f)}); scene.CreateEntity("FillLight") diff --git a/Engine/include/Engine/Assets/AssetManager.h b/Engine/include/Engine/Assets/AssetManager.h index 9a5a579..dd8bfb2 100644 --- a/Engine/include/Engine/Assets/AssetManager.h +++ b/Engine/include/Engine/Assets/AssetManager.h @@ -153,6 +153,9 @@ namespace Engine { Model* GetModel(ModelHandle h) const { return m_Models.Get(h); } void UnloadModel(ModelHandle h) { m_Models.Unload(h); } + // 程序化平面(地面等),同一 (size, texture) 只创建一次 + ModelHandle CreatePlane(f32 size, const std::string& texturePath); + AssetCache& Models() { return m_Models; } // 后续扩展点:TextureHandle / MaterialHandle / ShaderHandle diff --git a/Engine/include/Engine/Renderer/Model.h b/Engine/include/Engine/Renderer/Model.h index dc2aae0..c7ea118 100644 --- a/Engine/include/Engine/Renderer/Model.h +++ b/Engine/include/Engine/Renderer/Model.h @@ -32,6 +32,12 @@ namespace Engine DescriptorManager& descriptorManager, vk::DescriptorSetLayout textureLayout, const std::string& path); + // 程序化构造:单 quad 平面(XZ 平面朝上),不经过 assimp + static Scope CreatePlane(Device& device, MemoryAllocator& allocator, + DescriptorManager& descriptorManager, + vk::DescriptorSetLayout textureLayout, + f32 size, const std::string& texturePath); + ~Model(); Model(const Model&) = delete; @@ -40,6 +46,10 @@ namespace Engine const std::vector>& GetMeshes() const { return m_Meshes; } private: + struct ProceduralTag {}; + Model(ProceduralTag, Device& device, MemoryAllocator& allocator, + DescriptorManager& descriptorManager, + vk::DescriptorSetLayout textureLayout); void LoadModel(const std::string& path); void ProcessNode(aiNode* node, const aiScene* scene); Scope ProcessMesh(aiMesh* mesh, const aiScene* scene); diff --git a/Engine/src/Assets/AssetManager.cpp b/Engine/src/Assets/AssetManager.cpp index d0de0d2..40122bc 100644 --- a/Engine/src/Assets/AssetManager.cpp +++ b/Engine/src/Assets/AssetManager.cpp @@ -28,4 +28,14 @@ namespace Engine::Assets { }); } + ModelHandle AssetManager::CreatePlane(f32 size, const std::string& texturePath) + { + const std::string key = "procedural://plane/" + + std::to_string(size) + "/" + texturePath; + return m_Models.Load(key, [&](const std::string&) -> Scope { + return Model::CreatePlane(m_Device, m_Allocator, m_Descriptors, + m_TextureLayout, size, texturePath); + }); + } + } // namespace Engine::Assets diff --git a/Engine/src/Renderer/Model.cpp b/Engine/src/Renderer/Model.cpp index 0ef4b9d..42e0182 100644 --- a/Engine/src/Renderer/Model.cpp +++ b/Engine/src/Renderer/Model.cpp @@ -34,6 +34,44 @@ Model::Model(Device& device, MemoryAllocator& allocator, LoadModel(path); } +Model::Model(ProceduralTag, Device& device, MemoryAllocator& allocator, + DescriptorManager& descriptorManager, + vk::DescriptorSetLayout textureLayout) + : m_Device(device) + , m_Allocator(allocator) + , m_DescriptorManager(descriptorManager) + , m_TextureLayout(textureLayout) +{ + m_DefaultTexture = CreateScope( + m_Device, m_Allocator, "assets/textures/default.tga"); +} + +Scope Model::CreatePlane(Device& device, MemoryAllocator& allocator, + DescriptorManager& descriptorManager, + vk::DescriptorSetLayout textureLayout, + f32 size, const std::string& texturePath) +{ + auto model = Scope(new Model(ProceduralTag{}, device, allocator, + descriptorManager, textureLayout)); + + auto* texture = model->GetOrLoadTexture(texturePath); + auto material = CreateScope(device, descriptorManager, + textureLayout, texture); + + const f32 h = size * 0.5f; + std::vector vertices = { + {{-h, 0.0f, -h}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}}, + {{ h, 0.0f, -h}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}, + {{ h, 0.0f, h}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}}, + {{-h, 0.0f, h}, {0.0f, 1.0f, 0.0f}, {0.0f, 1.0f}}, + }; + std::vector indices = {0, 2, 1, 0, 3, 2}; + + model->m_Meshes.push_back(CreateScope( + device, allocator, vertices, indices, std::move(material))); + return model; +} + Model::~Model() = default; void Model::LoadModel(const std::string& path) diff --git a/Engine/src/Renderer/Renderer.cpp b/Engine/src/Renderer/Renderer.cpp index cc7e7ae..e72de80 100644 --- a/Engine/src/Renderer/Renderer.cpp +++ b/Engine/src/Renderer/Renderer.cpp @@ -547,14 +547,23 @@ namespace Engine } // 1.5 光空间矩阵:主光(lights[0])视角的正交盒包住场景。 - // 光源"位置" = 光方向反向拉远 30 个单位,看向原点。 + // lightDir 是光的"传播方向"(指向场景),光源位置必须沿其反方向 + // 退回(-lightDir * 距离)——方向搞反会变成"从地面下方往上打光", + // 地面永远是第一个被照到的面,影子永远不存在。 // 深度重映射 [-1,1] → [0,1](Vulkan clip volume),与相机投影同理。 glm::vec3 lightDir(ubo.lights[0].direction); if (glm::length(lightDir) > 0.0f) { + lightDir = glm::normalize(lightDir); + + // up 向量不能与视线平行(垂直光时 lookAt 退化成 NaN 矩阵) + glm::vec3 up = (std::abs(lightDir.y) > 0.99f) + ? glm::vec3(0.0f, 0.0f, 1.0f) + : glm::vec3(0.0f, 1.0f, 0.0f); + glm::mat4 lightView = glm::lookAt( - lightDir * 30.0f, // 沿光方向反推的光源位置 + -lightDir * 30.0f, // 光源位置:逆传播方向退回 glm::vec3(0.0f), // 看向原点 - glm::vec3(0.0f, 1.0f, 0.0f)); + up); glm::mat4 lightProj = glm::ortho( -20.0f, 20.0f, -20.0f, 20.0f, 0.1f, 100.0f);