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);