gpt4 book ai didi

c++ - 计算紧密正交投影矩阵以进行阴影映射

转载 作者:行者123 更新时间:2023-12-02 10:18:31 26 4
gpt4 key购买 nike

我正在尝试计算相机周围的紧密正交投影,以获得更好的阴影贴图。我首先使用基本三角函数,使用相机的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;
}

然后,我在光 View 中计算视锥,并计算每个轴上的最小和最大点,以计算正交投影的边界框,如下所示:
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)
max.x = frustumToLightView[i].x;
if (frustumToLightView[i].y > max.y)
max.y = frustumToLightView[i].y;
if (frustumToLightView[i].z > max.z)
max.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 + farRight - farTop;
m_RightFarTop = farCenter + farRight + farTop;
m_LeftFarBottom = farCenter - farRight - farTop;
m_LeftFarTop = farCenter - farRight + farTop;
}

创建正交投影时,还按如下所示翻转 z坐标:
return glm::ortho(min.x, max.x, min.y, max.y, -min.z, -max.z);

然而,深度图仍然没有任何表现。有任何想法吗?
这是捕获的结果,如您所见,左上角的四边形显示了阴影贴图,这是完全错误的,即使可以在对象本身上绘制阴影也可以看出:
https://gfycat.com/brightwealthybass

(阴影贴图值的拖尾只是我使用的gif压缩程序的一种产物,它并不是真的发生,因此我不清除FBO的z缓冲区就没有问题了)

编辑2::
好的, GetFov()返回的是度数而不是弧度。
我还尝试使用以下代码从NDC转换为世界空间:
    glm::mat4 inverseProjectViewMatrix = glm::inverse(camera.GetProjection() * camera.GetView());

std::array<glm::vec4, 8> NDC =
{
glm::vec4{-1.0f, -1.0f, -1.0f, 1.0f},
glm::vec4{1.0f, -1.0f, -1.0f, 1.0f},
glm::vec4{-1.0f, 1.0f, -1.0f, 1.0f},
glm::vec4{1.0f, 1.0f, -1.0f, 1.0f},
glm::vec4{-1.0f, -1.0f, 1.0f, 1.0f},
glm::vec4{1.0f, -1.0f, 1.0f, 1.0f},
glm::vec4{-1.0f, 1.0f, 1.0f, 1.0f},
glm::vec4{1.0f, 1.0f, 1.0f, 1.0f},
};

for (size_t i = 0; i < NDC.size(); i++)
{
NDC[i] = inverseProjectViewMatrix * NDC[i];
NDC[i] /= NDC[i].w;
}

对于平截头体的远坐标,它们等于我对平截头体的计算,但是对于近角,它们不起作用,就好像我对近角的计算减半了2(仅适用于x和y)。
例如:
右上角附近:
我的计算结果- {0.055, 0.041, 2.9}逆NDC yield - {0.11, 0.082, 2.8}
所以我不确定我的计算出了什么问题,也许您可​​以指出?
即使使用NDC反向坐标,我也尝试按以下方式使用它们:
glm::mat4 DirectionalLight::GetProjection(const Camera& camera) const
{
glm::mat4 lightView = GetView();

glm::mat4 inverseProjectViewMatrix = glm::inverse(camera.GetProjection() * camera.GetView());

std::array<glm::vec4, 8> NDC =
{
glm::vec4{-1.0f, -1.0f, 0.0f, 1.0f},
glm::vec4{1.0f, -1.0f, 0.0f, 1.0f},
glm::vec4{-1.0f, 1.0f, 0.0f, 1.0f},
glm::vec4{1.0f, 1.0f, 0.0f, 1.0f},
glm::vec4{-1.0f, -1.0f, 1.0f, 1.0f},
glm::vec4{1.0f, -1.0f, 1.0f, 1.0f},
glm::vec4{-1.0f, 1.0f, 1.0f, 1.0f},
glm::vec4{1.0f, 1.0f, 1.0f, 1.0f},
};

for (size_t i = 0; i < NDC.size(); i++)
{
NDC[i] = lightView * inverseProjectViewMatrix * NDC[i];
NDC[i] /= NDC[i].w;
}

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

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

仍然有不好的结果:
https://gfycat.com/negativemalealtiplanochinchillamouse

最佳答案

让我们从这里的视锥计算开始:

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约定使用的 View 空间是相机正朝负z方向注视的,但是 zNearzFar参数被解释为沿观察方向的距离,因此实际的观看量范围为viewt中的 -zFar, -zNear。您必须翻转 z维度的符号才能获得您要查找的实际边界框。

关于c++ - 计算紧密正交投影矩阵以进行阴影映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61125361/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com