Я пытаюсь вычислить плотную орто-проекцию вокруг камеры для лучшего отображения теней. Сначала я рассчитываю усеченную камеру на 8 точек в мировом пространстве, используя тригонометрию basi c, используя параметры fov, position, right, forward, near и far камеры следующим образом:
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)
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-компрессора, который я использовал, но на самом деле этого не происходит, поэтому у меня нет проблем, если я не очищу z-буфер FBO)
EDIT2: : Ладно, несколько вещей GetFov()
вернули градусы, а не радианы .. изменили это. Я также пробую преобразование из ND C в мировое пространство с помощью следующего кода:
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). Например: RIGHT TOP NEAR CORNER: мой расчет дает - {0.055, 0.041, 2.9}
обратный ND C доходность - {0.11, 0.082, 2.8}
Так что я не уверен, где мои вычисления ошиблись, может быть, вы могли бы указать? Даже с инвертированными координатами ND C я пытался использовать их следующим образом:
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