计算阴影映射的紧密正交投影矩阵

问题描述 投票:1回答:1

我正在尝试计算相机周围的紧密正交投影,以获得更好的阴影贴图。我首先使用基本三角函数使用相机的fov,position,right,forward,near和far参数来计算相机在世界空间中的视锥8点,如下所示:

PerspectiveFrustum::PerspectiveFrustum(const Camera* camera)
{
    float height = tanf(camera->GetFov() / 2.0f) * camera->GetNear();
    float width = height * Screen::GetWidth() / Screen::GetHeight();
    glm::vec3 nearTop = camera->GetUp() * camera->GetNear() * height;
    glm::vec3 nearRight = camera->GetRight() * camera->GetNear() * width;
    glm::vec3 nearCenter = camera->GetEye() + camera->GetForward() * camera->GetNear();
    glm::vec3 farTop = camera->GetUp() * camera->GetFar() * height;
    glm::vec3 farRight = camera->GetRight() * camera->GetFar() * width;
    glm::vec3 farCenter = camera->GetEye() + camera->GetForward() * camera->GetFar();
    m_RightNearBottom = nearCenter + nearRight - nearTop;
    m_RightNearTop = nearCenter + nearRight + nearTop;
    m_LeftNearBottom = nearCenter - nearRight - nearTop;
    m_LeftNearTop = nearCenter - nearRight + nearTop;
    m_RightFarBottom = farCenter + nearRight - nearTop;
    m_RightFarTop = farCenter + nearRight + nearTop;
    m_LeftFarBottom = farCenter - nearRight - nearTop;
    m_LeftFarTop = farCenter - nearRight + nearTop;
}

然后我在光视图中计算视锥,并计算每个轴上的最小和最大点,以如下方式计算正交投影的边界框:

inline glm::mat4 GetView() const
{
    return glm::lookAt(m_Position, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
}

glm::mat4 DirectionalLight::GetProjection(const Camera& camera) const
{
    PerspectiveFrustum frustum = camera.GetFrustum();
    glm::mat4 lightView = GetView();
    std::array<glm::vec3, 8> frustumToLightView
    {
        lightView * glm::vec4(frustum.m_RightNearBottom, 1.0f),
        lightView * glm::vec4(frustum.m_RightNearTop, 1.0f),
        lightView * glm::vec4(frustum.m_LeftNearBottom, 1.0f),
        lightView * glm::vec4(frustum.m_LeftNearTop, 1.0f),
        lightView * glm::vec4(frustum.m_RightFarBottom, 1.0f),
        lightView * glm::vec4(frustum.m_RightFarTop, 1.0f),
        lightView * glm::vec4(frustum.m_LeftFarBottom, 1.0f),
        lightView * glm::vec4(frustum.m_LeftFarTop, 1.0f)
    };

    glm::vec3 min{ INFINITY, INFINITY, INFINITY };
    glm::vec3 max{ -INFINITY, -INFINITY, -INFINITY };
    for (unsigned int i = 0; i < frustumToLightView.size(); i++)
    {
        if (frustumToLightView[i].x < min.x)
            min.x = frustumToLightView[i].x;
        if (frustumToLightView[i].y < min.y)
            min.y = frustumToLightView[i].y;
        if (frustumToLightView[i].z < min.z)
            min.z = frustumToLightView[i].z;

        if (frustumToLightView[i].x > max.x)
            min.x = frustumToLightView[i].x;
        if (frustumToLightView[i].y > max.y)
            min.y = frustumToLightView[i].y;
        if (frustumToLightView[i].z > max.z)
            min.z = frustumToLightView[i].z;
    }
    return glm::ortho(min.x, max.x, min.y, max.y, min.z, max.z);
}

这样做给了我空的阴影贴图,所以明显有问题,我没有做正确的事。有人可以告诉我我在做什么错,为什么吗?

编辑:如前所述,我对平截头体的计算是错误的,我将其更改为以下内容:

PerspectiveFrustum::PerspectiveFrustum(const Camera* camera)
{
    float nearHalfHeight = tanf(camera->GetFov() / 2.0f) * camera->GetNear();
    float nearHalfWidth = nearHalfHeight * Screen::GetWidth() / Screen::GetHeight();
    float farHalfHeight = tanf(camera->GetFov() / 2.0f) * camera->GetFar();
    float farHalfWidth = farHalfHeight * Screen::GetWidth() / Screen::GetHeight();

    glm::vec3 nearCenter = camera->GetEye() + camera->GetForward() * camera->GetNear();
    glm::vec3 nearTop = camera->GetUp() * nearHalfHeight;
    glm::vec3 nearRight = camera->GetRight() * nearHalfWidth;

    glm::vec3 farCenter = camera->GetEye() + camera->GetForward() * camera->GetFar();
    glm::vec3 farTop = camera->GetUp() * farHalfHeight;
    glm::vec3 farRight = camera->GetRight() * farHalfWidth;

    m_RightNearBottom = nearCenter + nearRight - nearTop;
    m_RightNearTop = nearCenter + nearRight + nearTop;
    m_LeftNearBottom = nearCenter - nearRight - nearTop;
    m_LeftNearTop = nearCenter - nearRight + nearTop;
    m_RightFarBottom = farCenter + nearRight - nearTop;
    m_RightFarTop = farCenter + nearRight + nearTop;
    m_LeftFarBottom = farCenter - nearRight - nearTop;
    m_LeftFarTop = farCenter - nearRight + nearTop;
}

[如下创建正交投影时也翻转z坐标:

return glm::ortho(min.x, max.x, min.y, max.y, -min.z, -max.z);

然而,深度图仍然没有任何内容。有什么想法吗?

opengl shader shadow shadow-mapping directional-light
1个回答
1
投票

让我们从这里开始进行平截头体计算:

float height = tanf(camera->GetFov() / 2.0f) * camera->GetNear();
[...]
glm::vec3 nearTop = camera->GetUp() * camera->GetNear() * height;
[...]
glm::vec3 farTop = camera->GetUp() * camera->GetFar() * height;

这就是乘法中的一对多GetNear。从概念上讲,您可以height表示单位距离上的平截头体高度的一半(我仍然会用不同的方式命名),而无需将其投影到近平面,那么其余公式更有意义。

但是,整个方法从一开始就令人怀疑。要获得世界空间中的平截头角,只需简单地投影[-1,1]^3 NDC立方体的所有8个顶点。由于您要将其转换为光空间,因此甚至可以将其组合为单个矩阵m = lightView * inverse(projection * view),只是不要忘了在乘以NDC立方体顶点后再进行透视划分。

return glm::ortho(min.x, max.x, min.y, max.y, min.z, max.z);

标准GL约定使用相机正向 z方向注视的视图空间,但是zNearzFar参数被解释为沿视图方向的距离,因此实际的视图范围为视图空间中的-zFar, -zNear。您必须翻转z尺寸的符号,才能获得所需的实际边界框。

© www.soinside.com 2019 - 2024. All rights reserved.