fix: 阴影光源位置计算反向 + lookAt 垂直光退化——影子可见
根因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 缓存) + 主光调平让影子落在相机视野内。已截图验证影子可见。
This commit is contained in:
@@ -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<Model> {
|
||||
return Model::CreatePlane(m_Device, m_Allocator, m_Descriptors,
|
||||
m_TextureLayout, size, texturePath);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Engine::Assets
|
||||
|
||||
@@ -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<Texture>(
|
||||
m_Device, m_Allocator, "assets/textures/default.tga");
|
||||
}
|
||||
|
||||
Scope<Model> Model::CreatePlane(Device& device, MemoryAllocator& allocator,
|
||||
DescriptorManager& descriptorManager,
|
||||
vk::DescriptorSetLayout textureLayout,
|
||||
f32 size, const std::string& texturePath)
|
||||
{
|
||||
auto model = Scope<Model>(new Model(ProceduralTag{}, device, allocator,
|
||||
descriptorManager, textureLayout));
|
||||
|
||||
auto* texture = model->GetOrLoadTexture(texturePath);
|
||||
auto material = CreateScope<Material>(device, descriptorManager,
|
||||
textureLayout, texture);
|
||||
|
||||
const f32 h = size * 0.5f;
|
||||
std::vector<Vertex> 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<u32> indices = {0, 2, 1, 0, 3, 2};
|
||||
|
||||
model->m_Meshes.push_back(CreateScope<Mesh>(
|
||||
device, allocator, vertices, indices, std::move(material)));
|
||||
return model;
|
||||
}
|
||||
|
||||
Model::~Model() = default;
|
||||
|
||||
void Model::LoadModel(const std::string& path)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user