Название все объясняет, но подведем итог. Когда направление камеры не направлено вниз или вверх, отсечение Frustum работает нормально (я проверял количество вызовов отрисовки и т. д. через консоль). НО он не работает при взгляде вверх и вниз (похоже, что он не масштабируется должным образом, но я не уверен). Я загрузил это видео здесь, так что вам, вероятно, стоит его посмотреть. https://streamable.com/pjp0nh
Фрустум.h
struct BoundingBox
{
Vec center;
Vec extents;
};
struct Plane_t
{
Vec normal;
float distance;
};
struct Frustum_t
{
Plane_t topface;
Plane_t bottomface;
Plane_t rightface;
Plane_t leftface;
Plane_t farface;
Plane_t nearface;
};
Frustum_t MakeFrustumFromView(const Vec& pos, const Vec& dir, float fov, float near, float far);
bool isBoundingBoxOnForwardPlane(const BoundingBox& boundingbox, const Plane_t& plane);
bool IsBoundingBoxInFrustum(const BoundingBox& boundingbox, const Frustum_t& frustum, const Mat4& transform);
Фрустум.cpp
inline Plane_t MakeFrustumPlane(const Vec& point, const Vec& norm)
{
return { norm, norm.Dot(point) };
}
Frustum_t MakeFrustumFromView(const Vec& pos, const Vec& dir, float fov, float near, float far)
{
Vec normdir = dir.Normalize();
Vec upvec = { 0.0f, 1.0f, 0.0f };
Vec right = normdir.Cross(upvec).Normalize();
Vec up = right.Cross(normdir).Normalize();
Frustum_t frustum;
const float fHalfVSide = far * tanf(fov * DEG2RAD * 0.5f);
const float fHalfHSide = fHalfVSide * ((float)g_pGlobals->m_iScreenWidth / g_pGlobals->m_iScreenHeight);
const Vec fDirFar = normdir * far;
frustum.nearface = MakeFrustumPlane(pos + normdir * near, normdir);
frustum.farface = MakeFrustumPlane(pos + fDirFar, -normdir);
frustum.rightface = MakeFrustumPlane(pos, (fDirFar - right * fHalfHSide).Cross(up));
frustum.leftface = MakeFrustumPlane(pos, up.Cross(fDirFar + right * fHalfHSide));
frustum.topface = MakeFrustumPlane(pos, right.Cross(fDirFar - up * fHalfVSide));
frustum.bottomface = MakeFrustumPlane(pos, (fDirFar + up * fHalfVSide).Cross(right));
return frustum;
}
bool isBoundingBoxOnForwardPlane(const BoundingBox& boundingbox, const Plane_t& plane)
{
const float r = boundingbox.extents.x * fabsf(plane.normal.x) + boundingbox.extents.y * fabsf(plane.normal.y) + boundingbox.extents.z * fabsf(plane.normal.z);
return -r <= (plane.normal.Dot(boundingbox.center) - plane.distance);
}
bool IsBoundingBoxInFrustum(const BoundingBox& boundingbox, const Frustum_t& frustum, const Mat4& transform)
{
Vec4D globalCenter = transform * Vec4D(boundingbox.center, 1.0f);
Vec bbRight = Vec(transform.m0, transform.m1, transform.m2) * boundingbox.extents.x;
Vec bbUp = Vec(transform.m4, transform.m5, transform.m6) * boundingbox.extents.y;
Vec bbForward = -Vec(transform.m8, transform.m9, transform.m10) * boundingbox.extents.z;
const float newIi = fabsf(Vec(1.0f, 0.0f, 0.0f).Dot(bbRight)) + fabsf(Vec(1.0f, 0.0f, 0.0f).Dot(bbUp)) + fabsf(Vec(1.0f, 0.0f, 0.0f).Dot(bbForward));
const float newIj = fabsf(Vec(0.0f, 1.0f, 0.0f).Dot(bbRight)) + fabsf(Vec(0.0f, 1.0f, 0.0f).Dot(bbUp)) + fabsf(Vec(0.0f, 1.0f, 0.0f).Dot(bbForward));
const float newIk = fabsf(Vec(0.0f, 0.0f, 1.0f).Dot(bbRight)) + fabsf(Vec(0.0f, 0.0f, 1.0f).Dot(bbUp)) + fabsf(Vec(0.0f, 0.0f, 1.0f).Dot(bbForward));
const BoundingBox globalBoundingBox = { globalCenter.ToVec(), {newIi, newIj, newIk} };
return (isBoundingBoxOnForwardPlane(globalBoundingBox, frustum.leftface) &&
isBoundingBoxOnForwardPlane(globalBoundingBox, frustum.rightface) &&
isBoundingBoxOnForwardPlane(globalBoundingBox, frustum.topface) &&
isBoundingBoxOnForwardPlane(globalBoundingBox, frustum.bottomface) &&
isBoundingBoxOnForwardPlane(globalBoundingBox, frustum.nearface) &&
isBoundingBoxOnForwardPlane(globalBoundingBox, frustum.farface));
}
Чтобы использовать код, я поместил ограничивающие рамки своих сеток в функцию IsBoundingBoxInFrustum рядом с усеченной пирамидой и преобразованием экземпляра сетки (которое включает перемещение, масштабирование и вращение). Также я хотел бы отметить, что для этого кода я следил за LearnOpenGL.
@BDL У меня уже есть все значения, включая перекрестное произведение upvec иnormaldir, как вы упомянули. Я также хотел бы отметить, что я следовал руководству Learnopengl...





В следующий раз предоставьте MCVE.
Используйте библиотеку GLM вместо собственного кода.
Способ вычисления левой, правой, верхней и нижней плоскостей отсечения мне немного странен. Предположим, у нас есть:
----------*-b--*------------ far
| /
a| /
----------------- near
| /
|/
/
pos-----x
|
|
z
если a — вектор от дальней плоскости до pos, а b — масштабированный правый вектор, вектор направления правой плоскости отсечения равен:
fDirFar + right * fHalfHSide
(как видите, знак изменился). Те же исправления, подобные этому, следует применить для левой, верхней и нижней плоскостей.
transform, который содержит функции перемещения, масштабирования и вращения (перемещение из локального пространства в мировое), поэтому я предполагаю, что она не содержит преобразования представления. Поскольку вы тестируете ограничивающую рамку вашей сетки на соответствие плоскостям отсечения, выраженным в пространстве обзора, определенном размещением камеры в точке pos, преобразование сетки должно также включать преобразование вида.Проблема заключалась в том, что я не использовал GLM (да, по какой-то причине в математической библиотеке, которую я использовал, были некоторые проблемы, которые я не мог понять.)
Если вы посмотрите точно вверх или вниз, векторное произведение upvec иnormaldir будет нулевым вектором. Я предполагаю, что люди не голосуют за ваш вопрос, потому что вы могли легко заметить это, установив точку останова или выведя свои значения на консоль. Вопрос о том, как решить проблему, если вы знаете, что ее вызывает, возможно, стоит задать здесь.