向上或向下看时视锥体剔除会中断

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

标题已经说明了一切,但还是总结一下。当相机的方向不是向下或向上时,视锥体剔除工作正常(我通过控制台检查了绘制调用的数量等)。 但向上和向下查看时它不起作用(看起来好像没有正确缩放,但我不确定)。 我已将其视频上传到此处,因此您应该观看一下。 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 来编写此代码。

c++ opengl frustum glfrustum
1个回答
0
投票

看辅助图:

----------------far
               /
              /
    ---------near
        |   /
fDirNear|  /  <--- rightPlaneDir
        | /
        |/
       pos----------- x
        |
        |
        z

如果是右裁剪平面,您必须获得该平面的方向向量(rightPlaneDir)。它可以计算为:近平面的垂直向量和右向量的和:

const Vec fDirNear = normdir * near; // vector to near plane
Vec rightPlaneDir == fDirNear + right * fHalfHSide;

当你有了裁剪平面的方向向量和向量up时,通过叉积你可以确定垂直于裁剪平面的向量:

frustum.rightface = MakeFrustumPlane(pos, (rightPlaneDir).Cross(up));

相同的修复应该应用于左、上、下平面,即在计算左/上/下向量时使用 fNearDir 并更改符号。

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