From 3907868417261700b6ad5dac50e3cac286295914 Mon Sep 17 00:00:00 2001 From: atdunbg Date: Tue, 22 Sep 2026 15:35:52 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=98=B4=E5=BD=B1=E4=B8=8D=E5=8F=AF?= =?UTF-8?q?=E8=A7=81=E7=9A=84=E7=9C=9F=E6=AD=A3=E6=A0=B9=E5=9B=A0=E2=80=94?= =?UTF-8?q?=E2=80=94=E6=B7=B1=E5=BA=A6=E9=87=8D=E6=98=A0=E5=B0=84=E7=9F=A9?= =?UTF-8?q?=E9=98=B5=E5=85=AC=E5=BC=8F=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 诊断方法:日志打印 lightVP/lightProj/lightView 矩阵分解定位。 修复前 lightVP 在原点处 z=-14.70(应 ∈[-1,1])——深度全部越界被裁。 根因:remap 公式把偏移量 0.5 错加进了 z 缩放系数: 错:M[2][2] = M[2][2]*0.5 + 0.5 (0.5 混入 z 系数) 对:M[2][2] *= 0.5 (z' = 0.5z + 0.5w,偏移属于 w 列) M[3][2] = M[3][2]*0.5 + 0.5 正交投影 w=1,错误的 0.5*z 项使深度随距离线性发散,shadow map 内所有深度越界,采样对比永不成立。透视投影同错(被 w 除法稀释 成近似常数偏移,此前一直"看起来正常")。修复 Camera + lightProj。 同批修复: - 深度 bias 1.25 → 0.0025/0.003(NDC 空间,1.25 会饱和全部深度) - 光源位置 lightDir*30 → -lightDir*30(原方向反了:从地下往上照) - lookAt 垂直光 up 退化(叉积为零 → NaN 矩阵) - 地面改纯色纹理(Texture::CreateSolidColor,1x1 程序化生成) 矩阵诊断验证:lightVP 原点深度 -14.70 → 0.30,物体(0.30) < 地面(0.32) 遮挡关系正确。validation 零错误零警告。 --- App/main.cpp | 4 +- Engine/include/Engine/Assets/AssetManager.h | 4 +- Engine/include/Engine/Renderer/Model.h | 4 +- Engine/include/Engine/Vulkan/Texture.h | 15 +++++++ Engine/src/Assets/AssetManager.cpp | 9 ++-- Engine/src/Renderer/Camera.cpp | 6 +++ Engine/src/Renderer/Model.cpp | 9 ++-- Engine/src/Renderer/Renderer.cpp | 12 ++++-- Engine/src/Vulkan/Texture.cpp | 46 +++++++++++++++++---- 9 files changed, 86 insertions(+), 23 deletions(-) diff --git a/App/main.cpp b/App/main.cpp index 8ce9921..8b0f54b 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -42,8 +42,8 @@ protected: // 模型资产(AssetManager 负责加载/缓存/生命周期,返回句柄) auto backpack = GetAssets().LoadModel("assets/models/backpack/backpack.obj"); - // 地面:接收阴影 - auto ground = GetAssets().CreatePlane(30.0f, "assets/textures/default.tga"); + // 地面:接收阴影(中性灰纯色,衬托影子) + auto ground = GetAssets().CreatePlane(30.0f, {140, 140, 145}); auto groundEntity = scene.CreateEntity("Ground"); groundEntity.AddComponent().world = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, -2.0f, 0.0f)); diff --git a/Engine/include/Engine/Assets/AssetManager.h b/Engine/include/Engine/Assets/AssetManager.h index dd8bfb2..a3edd40 100644 --- a/Engine/include/Engine/Assets/AssetManager.h +++ b/Engine/include/Engine/Assets/AssetManager.h @@ -153,8 +153,8 @@ 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); + // 程序化平面(地面等),纯色贴图,同一 (size, color) 只创建一次 + ModelHandle CreatePlane(f32 size, std::array color); AssetCache& Models() { return m_Models; } diff --git a/Engine/include/Engine/Renderer/Model.h b/Engine/include/Engine/Renderer/Model.h index c7ea118..0c1ad51 100644 --- a/Engine/include/Engine/Renderer/Model.h +++ b/Engine/include/Engine/Renderer/Model.h @@ -32,11 +32,11 @@ namespace Engine DescriptorManager& descriptorManager, vk::DescriptorSetLayout textureLayout, const std::string& path); - // 程序化构造:单 quad 平面(XZ 平面朝上),不经过 assimp + // 程序化构造:单 quad 平面(XZ 平面朝上),纯色贴图,不经过 assimp static Scope CreatePlane(Device& device, MemoryAllocator& allocator, DescriptorManager& descriptorManager, vk::DescriptorSetLayout textureLayout, - f32 size, const std::string& texturePath); + f32 size, std::array color); ~Model(); diff --git a/Engine/include/Engine/Vulkan/Texture.h b/Engine/include/Engine/Vulkan/Texture.h index 5247033..7b2810e 100644 --- a/Engine/include/Engine/Vulkan/Texture.h +++ b/Engine/include/Engine/Vulkan/Texture.h @@ -9,6 +9,7 @@ #include #include +#include #include #include "MemoryAllocator.h" @@ -21,6 +22,12 @@ namespace Engine { class Texture { public: Texture(Device& device, MemoryAllocator& allocator, const std::string& path); + + // 程序化纯色纹理(不经过磁盘),width/height = 1 时即单色 + static Scope CreateSolidColor(Device& device, MemoryAllocator& allocator, + u32 width, u32 height, + std::array rgba); + ~Texture(); Texture(const Texture&) = delete; @@ -49,6 +56,14 @@ namespace Engine { MemoryAllocator::Allocation m_ImageAllocation; MemoryAllocator& m_Allocator; + + // 公共上传路径:staging → device local image → view/sampler + // (文件加载与程序化创建共用) + void InitFromPixels(const void* pixels, u32 width, u32 height, + const std::string& debugName); + + struct UploadTag {}; + Texture(UploadTag, Device& device, MemoryAllocator& allocator); }; } // namespace Engine diff --git a/Engine/src/Assets/AssetManager.cpp b/Engine/src/Assets/AssetManager.cpp index 40122bc..077ec23 100644 --- a/Engine/src/Assets/AssetManager.cpp +++ b/Engine/src/Assets/AssetManager.cpp @@ -28,13 +28,16 @@ namespace Engine::Assets { }); } - ModelHandle AssetManager::CreatePlane(f32 size, const std::string& texturePath) + ModelHandle AssetManager::CreatePlane(f32 size, std::array color) { const std::string key = "procedural://plane/" + - std::to_string(size) + "/" + texturePath; + std::to_string(size) + "/" + + std::to_string(color[0]) + "/" + + std::to_string(color[1]) + "/" + + std::to_string(color[2]); return m_Models.Load(key, [&](const std::string&) -> Scope { return Model::CreatePlane(m_Device, m_Allocator, m_Descriptors, - m_TextureLayout, size, texturePath); + m_TextureLayout, size, color); }); } diff --git a/Engine/src/Renderer/Camera.cpp b/Engine/src/Renderer/Camera.cpp index 12c0c20..7a889cd 100644 --- a/Engine/src/Renderer/Camera.cpp +++ b/Engine/src/Renderer/Camera.cpp @@ -58,6 +58,12 @@ namespace Engine { { auto proj = glm::perspective(glm::radians(45.0f), aspect, nearZ, farZ); proj[1][1] *= -1.0f; // Vulkan Y 翻转 + + // 深度重映射 z' = 0.5z + 0.5w(Vulkan clip volume [0,1]): + // 缩放作用于 z 系数 [2][2],偏移作用于 w 列系数 [3][2]。 + // 没有这一步:近半段视锥被裁掉、深度精度减半。 + proj[2][2] *= 0.5f; + proj[3][2] = proj[3][2] * 0.5f + 0.5f; return proj; } diff --git a/Engine/src/Renderer/Model.cpp b/Engine/src/Renderer/Model.cpp index 42e0182..9593021 100644 --- a/Engine/src/Renderer/Model.cpp +++ b/Engine/src/Renderer/Model.cpp @@ -49,14 +49,17 @@ Model::Model(ProceduralTag, Device& device, MemoryAllocator& allocator, Scope Model::CreatePlane(Device& device, MemoryAllocator& allocator, DescriptorManager& descriptorManager, vk::DescriptorSetLayout textureLayout, - f32 size, const std::string& texturePath) + f32 size, std::array color) { auto model = Scope(new Model(ProceduralTag{}, device, allocator, descriptorManager, textureLayout)); - auto* texture = model->GetOrLoadTexture(texturePath); + // 纯色纹理(1x1):不依赖磁盘资产 + auto texture = Texture::CreateSolidColor(device, allocator, 1, 1, + {color[0], color[1], color[2], 255}); auto material = CreateScope(device, descriptorManager, - textureLayout, texture); + textureLayout, texture.get()); + model->m_TextureCache["__solid_color"] = std::move(texture); const f32 h = size * 0.5f; std::vector vertices = { diff --git a/Engine/src/Renderer/Renderer.cpp b/Engine/src/Renderer/Renderer.cpp index e72de80..96a3420 100644 --- a/Engine/src/Renderer/Renderer.cpp +++ b/Engine/src/Renderer/Renderer.cpp @@ -292,9 +292,12 @@ namespace Engine cmd.setDepthBoundsTestEnable(VK_FALSE); cmd.setStencilTestEnable(VK_FALSE); - // 深度偏移:缓解 shadow acne(斜率缩放) + // 深度偏移:缓解 shadow acne。 + // 注意:bias 作用在 NDC 深度 [0,1] 上!正交阴影图的合理量级 + // 是 0.001~0.005——写 1.25 会把全部深度饱和成 1.0, + // shadow map 变成纯白,采样对比永远不成立(无影子)。 cmd.setDepthBiasEnable(VK_TRUE); - cmd.setDepthBias(1.25f, 0.0f, 1.75f); + cmd.setDepthBias(0.0025f, 0.0f, 0.003f); cmd.setPolygonModeEXT(vk::PolygonMode::eFill); cmd.setCullMode(vk::CullModeFlagBits::eNone); @@ -567,7 +570,10 @@ namespace Engine glm::mat4 lightProj = glm::ortho( -20.0f, 20.0f, -20.0f, 20.0f, 0.1f, 100.0f); - lightProj[2][2] = lightProj[2][2] * 0.5f + 0.5f; + // 深度重映射 z' = 0.5z + 0.5w:缩放作用于 z 系数 [2][2], + // 偏移作用于 w 列系数 [3][2]!之前把 0.5 错加进 [2][2], + // 正交投影(w=1)下深度随距离发散,全部超出裁剪范围。 + lightProj[2][2] *= 0.5f; lightProj[3][2] = lightProj[3][2] * 0.5f + 0.5f; m_LightSpaceMatrix = lightProj * lightView; diff --git a/Engine/src/Vulkan/Texture.cpp b/Engine/src/Vulkan/Texture.cpp index cf3f8e4..ef08856 100644 --- a/Engine/src/Vulkan/Texture.cpp +++ b/Engine/src/Vulkan/Texture.cpp @@ -28,8 +28,40 @@ namespace Engine return; } - m_Width = static_cast(width); - m_Height = static_cast(height); + InitFromPixels(pixels, static_cast(width), static_cast(height), path); + + stbi_image_free(pixels); + } + + Texture::Texture(UploadTag, Device& device, MemoryAllocator& allocator) + : m_Device(device), m_Allocator(allocator) + { + } + + Scope Texture::CreateSolidColor(Device& device, MemoryAllocator& allocator, + u32 width, u32 height, + std::array rgba) + { + auto tex = Scope(new Texture(UploadTag{}, device, allocator)); + + const size_t pixelCount = static_cast(width) * height; + std::vector pixels(pixelCount * 4, rgba[3]); // 全填 alpha,下面覆写 RGB + for (size_t i = 0; i < pixelCount; ++i) { + pixels[i * 4 + 0] = rgba[0]; + pixels[i * 4 + 1] = rgba[1]; + pixels[i * 4 + 2] = rgba[2]; + pixels[i * 4 + 3] = rgba[3]; + } + + tex->InitFromPixels(pixels.data(), width, height, "solid_color"); + return tex; + } + + void Texture::InitFromPixels(const void* pixels, u32 width, u32 height, + const std::string& debugName) + { + m_Width = width; + m_Height = height; vk::DeviceSize imageSize = static_cast(width) * static_cast(height) * 4; // RGBA8 @@ -42,7 +74,7 @@ namespace Engine vk::Buffer stagingBuffer = m_Device.GetDevice().createBuffer(stagingInfo); auto stagingReqs = m_Device.GetDevice().getBufferMemoryRequirements(stagingBuffer); - auto stagingAlloc = allocator.Allocate( + auto stagingAlloc = m_Allocator.Allocate( stagingReqs, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); @@ -52,8 +84,6 @@ namespace Engine // 3. 拷贝像素到 Staging Buffer std::memcpy(stagingAlloc.MappedPtr, pixels, static_cast(imageSize)); - stbi_image_free(pixels); - // 4. 创建 Device Local Image vk::ImageCreateInfo imageInfo; imageInfo.setImageType(vk::ImageType::e2D) @@ -70,7 +100,7 @@ namespace Engine m_Image = m_Device.GetDevice().createImage(imageInfo); auto imageReqs = m_Device.GetDevice().getImageMemoryRequirements(m_Image); - auto imageAlloc = allocator.Allocate(imageReqs, vk::MemoryPropertyFlagBits::eDeviceLocal); + auto imageAlloc = m_Allocator.Allocate(imageReqs, vk::MemoryPropertyFlagBits::eDeviceLocal); m_ImageAllocation = imageAlloc; @@ -185,10 +215,10 @@ namespace Engine m_Device.GetDevice().destroyFence(fence); m_Device.GetDevice().destroyCommandPool(pool); m_Device.GetDevice().destroyBuffer(stagingBuffer); - allocator.Free(stagingAlloc); + m_Allocator.Free(stagingAlloc); // staging 内存由 MemoryAllocator 统一释放 - ENGINE_INFO("Texture loaded: {} ({}x{})", path, m_Width, m_Height); + ENGINE_INFO("Texture uploaded: {} ({}x{})", debugName, m_Width, m_Height); // 显存占用 = 宽 * 高 * 4 字节(RGBA8)