c++opengllinear-algebraglm-mathperspectivecamera

Problem with Bounding Sphere Frustum Culling


Bellow I am going to post my frustum culling code for culling bounding spheres. My algorithm should be fairly simple and understandable: The update Method (called every frame update) updates the planes of the frustum by calculating the corners of the frustum planes and with them the corresponding normals. The normals should all point to the center of the frustum.

In the inFrustum method the signed distance to the individual planes is calculated and my idea was, that if a point has a positive distance to the left as well as the right plane it has to lie in between those two. Doing this with left-right, top-bottom and near-far should determine weather the point is visible or not. And this works just fine as long as I don't turn more than 180°, then points that lie in the frustum become invisible. cam->up and cam->dir are the up vector of the camera and the vector of the camera's view ray, near, far and fov_degree are the corresponding projection variables. Do you have an idea why this doesn't work as it should? The problem also seams to correspond with the position of the cam.

class plane{
private:
    float a, b, c, d;
public:
    void set(glm::vec3 p0, glm::vec3 p1, glm::vec3 p2){
        using namespace glm;
        vec3 v = p1-p0;
        vec3 u = p2-p0;
        vec3 n = normalize(cross(v, u));
        a = n.x;
        b = n.y;
        c = n.z;
        d = dot(-n, p0);
    }
    void set(glm::vec3 p, glm::vec3 n){
        a = n.x;
        b = n.y;
        c = n.z;
        d = p.x*n.x + p.y*n.y + p.z*n.z;
    }
    float distanceTo(glm::vec3 p) const{
        return a*p.x + b*p.y + c*p.z + d;
    }
};
class frustumCulling{
private:
    plane front, back, left, right, top, bottom;
public:
    frustumCulling(Camera& cam){
        update(cam);
    }
    void update(Camera& cam){
        using namespace glm;
        vec3 ndir = normalize(cam->dir);
        vec3 nup = normalize(cam->up);
        vec3 nright = normalize(cross(cam->dir, cam->up));
        //aligned planes
        vec3 onn = cam->pos + ndir*cam->near;
        vec3 onf = cam->pos + ndir*cam->far;
        front.set(onn, ndir);
        back.set(onf, -ndir);
        //far and near dimensions
        vec2 fardim = vec2(0.0f, cam->far*tan(radians(cam->fov_degree/2.0f)));
        fardim.x= fardim.y*cam->aspect_ratio();
        vec2 neardim = vec2(0.0f, cam->near*tan(radians(cam->fov_degree/2.0f)));
        neardim.x = neardim.y*cam->aspect_ratio();
        //view frustum corners //l = left, r = right, u = up, b=bottom, n= near, f = far
        vec3 n_lu = onn + nup*neardim.y*0.5f - nright*neardim.x*0.5f;
        vec3 n_ru = onn + nup*neardim.y*0.5f + nright*neardim.x*0.5f;
        vec3 n_rb = onn - nup*neardim.y*0.5f + nright*neardim.x*0.5f;
        vec3 n_lb = onn - nup*neardim.y*0.5f - nright*neardim.x*0.5f;


        vec3 f_lu = onf + nup*fardim.y*0.5f - nright*fardim.x*0.5f;
        vec3 f_ru = onf + nup*fardim.y*0.5f + nright*fardim.x*0.5f;
        vec3 f_rb = onf - nup*fardim.y*0.5f + nright*fardim.x*0.5f;
        vec3 f_lb = onf - nup*fardim.y*0.5f - nright*fardim.x*0.5f;
        //non aligned planes
        vec3 leftn = normalize(cross(n_lb-n_lu, f_lu-n_lu));
        left.set(n_lu, leftn);

        vec3 rightn = normalize(cross(n_ru-n_rb, f_rb-n_rb));
        right.set(n_ru, rightn);

        vec3 topn = normalize(cross(n_lu - n_ru, f_ru - n_ru));
        top.set(n_ru, topn);

        vec3 botn = normalize(cross(f_rb - n_rb, n_lb - n_rb));
        bottom.set(n_rb, topn);
    }
    bool inFrustum(glm::vec3 p, float radius){
        float dt = top.distanceTo(p);
        float db = bottom.distanceTo(p);
        float dl = left.distanceTo(p);
        float dr = right.distanceTo(p);
        float df = front.distanceTo(p);
        float dn = back.distanceTo(p);
        bool ix, iy, iz;
        ix = (dl > 0 && dr > 0) || glm::abs(dl) <= radius || glm::abs(dr) <= radius;
        iy = (dt > 0 && db > 0) || glm::abs(dt) <= radius || glm::abs(db) <= radius;
        iz = (df > 0 && dn > 0) || glm::abs(df) <= radius || glm::abs(dn) <= radius;

        //just for debugging; should be ix&&iy&&iz
        return ix;
    }
};

Solution

  • I solved it the problem was the d = p.x*n.x + p.y*n.y + p.z*n.z; in set, it should've been dot(-n, p) instead I did dot(n,p)