c++openglfrustumglfrustum

Frustum Culling breaks when looking up or down


The title explains it all, but to summarize. When the camera's direction is not pointing down or up, Frustum culling works fine (I checked the amount of draw calls etc thru console). BUT it is not working when looking up and down (it looks as if it is not scaling properly but I am not sure). I've uploaded the video of it here so you should probably watch it. https://streamable.com/pjp0nh

Frustum.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);

Frustum.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));
}

To use the code I put in my meshes' bounding boxes into the function IsBoundingBoxInFrustum along side the frustum and the transform OF the mesh instance (which includes translation, scale and rotation). Also, I'd like to mention that I followed LearnOpenGL for this code.


Solution

  • The problem was that I wasn't using GLM (Yes, for whatever reason the math lib I was using had some problems which I couldn't figure out.)