Frustum culling question

I got the code for a frustum culling class out of a book, and there was nothing in the way of explanation, so I really don’t understand how the thing does it’s job.

The terrain is broken up into square blocks, and my goal is to cull the non-visible blocks. I believe the code is good, because the demos in the book use it, and the culling works fine.

As far as I can tell, the code is testing each corner of the bounding box to see if any of them are currently visible, and if so, the block is visible. I think the problem is that my terrain blocks are large enough so that the camera can get in a position where some of the block is still visible, yet none of the corners are. The code listed is the method used to cull the blocks.

I don’t want to make the blocks smaller, so what are my options?

// x, y, z : center vertex of the block to be checked
// size : length of one side of the block to be checked

public boolean CubeFrustumTest( float x, float y,
float z, float size )
{
for( int i=0; i<6; i++ )
{
if( m_viewFrustum[i][0] * ( x-size ) +
m_viewFrustum[i][1] * ( y-size ) +
m_viewFrustum[i][2] * ( z-size ) +
m_viewFrustum[i][3] > 0 )
continue;

    if( m_viewFrustum[i][0] * ( x+size ) + 
        m_viewFrustum[i][1] * ( y-size ) +
        m_viewFrustum[i][2] * ( z-size ) +
        m_viewFrustum[i][3] > 0 )
        continue;
        
    if( m_viewFrustum[i][0] * ( x-size ) + 
        m_viewFrustum[i][1] * ( y+size ) +
        m_viewFrustum[i][2] * ( z-size ) +
        m_viewFrustum[i][3] > 0 )
        continue;
        
    if( m_viewFrustum[i][0] * ( x+size ) +
        m_viewFrustum[i][1] * ( y+size ) +
        m_viewFrustum[i][2] * ( z-size ) +
        m_viewFrustum[i][3] > 0 )
        continue;
        
    if( m_viewFrustum[i][0] * ( x-size ) +
        m_viewFrustum[i][1] * ( y-size ) + 
        m_viewFrustum[i][2] * ( z+size ) +
        m_viewFrustum[i][3] > 0 )
        continue;
        
    if( m_viewFrustum[i][0] * ( x+size ) +
        m_viewFrustum[i][1] * ( y-size ) +
        m_viewFrustum[i][2] * ( z+size ) +
        m_viewFrustum[i][3] > 0 )
        continue;
        
    if( m_viewFrustum[i][0] * ( x-size ) +
        m_viewFrustum[i][1] * ( y+size ) +
        m_viewFrustum[i][2] * ( z+size ) +
        m_viewFrustum[i][3] > 0 )
        continue;
        
    if( m_viewFrustum[i][0] * ( x+size ) +
        m_viewFrustum[i][1] * ( y+size ) +
        m_viewFrustum[i][2] * ( z+size ) +
        m_viewFrustum[i][3] > 0 )
        continue;

    return false;
}

return true;
}

If someone has a FrustumCuller that contains a method that would work in my case, I would be very pleased to recieve it.
kmyers@bardstowncable.net

Thanks :slight_smile:

[quote]As far as I can tell, the code is testing each corner of the bounding box to see if any of them are currently visible, and if so, the block is visible.
[/quote]
The code checks each corner of the box and if all of them is behind a plane it is not visible and returns false. The code looks correct. Try to verify that your parameters are correct. Make sure that x,y,z is in the center of the box and that size is the maximum of the box sides. If it still don’t work just multiply size with 2 :stuck_out_tongue:

I hate to ask anybody to dig through this, but if it can be verified that this code is correct, then I’m really doing something wrong, because even expanding the ‘size’ argument to the culling method call didn’t help :frowning:

Note: mx is the modelview matrix, and px is the projection matrix.



/* combine the two matrices by multiplying them */
                      clip[0]  = mx[0]*px[0]  + mx[1]*px[4]  + mx[2]*px[8]   + mx[3]*px[12];
                      clip[1]  = mx[0]*px[1]  + mx[1]*px[5]  + mx[2]*px[9]   + mx[3]*px[13];
                      clip[2]  = mx[0]*px[2]  + mx[1]*px[6]  + mx[2]*px[10]  + mx[3]*px[14];
                      clip[3]  = mx[0]*px[3]  + mx[1]*px[7]  + mx[2]*px[11]  + mx[3]*px[15];
                      
                      clip[4]  = mx[4]*px[0]  + mx[5]*px[4]  + mx[6]*px[8]   + mx[7]*px[12];
                      clip[5]  = mx[4]*px[1]  + mx[5]*px[5]  + mx[6]*px[9]   + mx[7]*px[13];
                      clip[6]  = mx[4]*px[2]  + mx[5]*px[6]  + mx[6]*px[10]  + mx[7]*px[14];
                      clip[7]  = mx[4]*px[3]  + mx[5]*px[7]  + mx[6]*px[11]  + mx[7]*px[15];
                      
                      clip[8]  = mx[8]*px[0]  + mx[9]*px[4]  + mx[10]*px[8]  + mx[11]*px[12];
                      clip[9]  = mx[8]*px[1]  + mx[9]*px[5]  + mx[10]*px[9]  + mx[11]*px[13];
                      clip[10] = mx[8]*px[2]  + mx[9]*px[6]  + mx[10]*px[10] + mx[11]*px[14];
                      clip[11] = mx[8]*px[3]  + mx[9]*px[7]  + mx[10]*px[11] + mx[11]*px[15];
                      
                      clip[12] = mx[12]*px[0] + mx[13]*px[4] + mx[14]*px[8]  + mx[15]*px[12];
                      clip[13] = mx[12]*px[1] + mx[13]*px[5] + mx[14]*px[9]  + mx[15]*px[13];
                      clip[14] = mx[12]*px[2] + mx[13]*px[6] + mx[14]*px[10] + mx[15]*px[14];
                      clip[15] = mx[12]*px[3] + mx[13]*px[7] + mx[14]*px[11] + mx[15]*px[15];
                      
                      //-------------------- 
                      
                      /* Extract the numbers for the RIGHT plane */
                      viewFrustum[FRUSTUM_RIGHT][0] = clip[3]  - clip[0];
                      viewFrustum[FRUSTUM_RIGHT][1] = clip[7]  - clip[4];
                      viewFrustum[FRUSTUM_RIGHT][2] = clip[11] - clip[8];
                      viewFrustum[FRUSTUM_RIGHT][3] = clip[15] - clip[12];
                      
                      /* Normalize the RIGHT plane */
                      norm = (float)Math.sqrt(
                              (viewFrustum[FRUSTUM_RIGHT][0] * viewFrustum[FRUSTUM_RIGHT][0]) +
                         (viewFrustum[FRUSTUM_RIGHT][1] * viewFrustum[FRUSTUM_RIGHT][1]) +
                         (viewFrustum[FRUSTUM_RIGHT][2] * viewFrustum[FRUSTUM_RIGHT][2])   );
                         
                      viewFrustum[FRUSTUM_RIGHT][0] /= norm;
                      viewFrustum[FRUSTUM_RIGHT][1] /= norm;
                      viewFrustum[FRUSTUM_RIGHT][2] /= norm;
                      viewFrustum[FRUSTUM_RIGHT][3] /= norm;
                      
                      //--------------------
                      
                      /* Extract the numbers for the LEFT plane */
                      viewFrustum[FRUSTUM_LEFT][0] = clip[3]  + clip[0];
                      viewFrustum[FRUSTUM_LEFT][1] = clip[7]  + clip[4];
                      viewFrustum[FRUSTUM_LEFT][2] = clip[11] + clip[8];
                      viewFrustum[FRUSTUM_LEFT][3] = clip[15] + clip[12];
                      
                      /* Normalize the LEFT plane */
                      norm = (float)Math.sqrt(
                              (viewFrustum[FRUSTUM_LEFT][0] * viewFrustum[FRUSTUM_LEFT][0]) +
                         (viewFrustum[FRUSTUM_LEFT][1] * viewFrustum[FRUSTUM_LEFT][1]) +
                         (viewFrustum[FRUSTUM_LEFT][2] * viewFrustum[FRUSTUM_LEFT][2])   );
                         
                      viewFrustum[FRUSTUM_LEFT][0] /= norm;
                      viewFrustum[FRUSTUM_LEFT][1] /= norm;
                      viewFrustum[FRUSTUM_LEFT][2] /= norm;
                      viewFrustum[FRUSTUM_LEFT][3] /= norm;
                      
                      //--------------------
                      
                      /* Extract the numbers for the BOTTOM plane */
                      viewFrustum[FRUSTUM_BOTTOM][0] = clip[3]  + clip[1];
                      viewFrustum[FRUSTUM_BOTTOM][1] = clip[7]  + clip[5];
                      viewFrustum[FRUSTUM_BOTTOM][2] = clip[11] + clip[9];
                      viewFrustum[FRUSTUM_BOTTOM][3] = clip[15] + clip[13];
                      
                      /* Normalize the BOTTOM plane */
                      norm = (float)Math.sqrt(
                              (viewFrustum[FRUSTUM_BOTTOM][0] * viewFrustum[FRUSTUM_BOTTOM][0]) +
                         (viewFrustum[FRUSTUM_BOTTOM][1] * viewFrustum[FRUSTUM_BOTTOM][1]) +
                         (viewFrustum[FRUSTUM_BOTTOM][2] * viewFrustum[FRUSTUM_BOTTOM][2])   );
                         
                      viewFrustum[FRUSTUM_BOTTOM][0] /= norm;
                      viewFrustum[FRUSTUM_BOTTOM][1] /= norm;
                      viewFrustum[FRUSTUM_BOTTOM][2] /= norm;
                      viewFrustum[FRUSTUM_BOTTOM][3] /= norm;
                      
                      //--------------------
                      
                      /* Extract the numbers for the TOP plane */
                      viewFrustum[FRUSTUM_TOP][0] = clip[3]  - clip[1];
                      viewFrustum[FRUSTUM_TOP][1] = clip[7]  - clip[5];
                      viewFrustum[FRUSTUM_TOP][2] = clip[11] - clip[9];
                      viewFrustum[FRUSTUM_TOP][3] = clip[15] - clip[13];
                      
                      /* Normalize the TOP plane */
                      norm = (float)Math.sqrt(
                              (viewFrustum[FRUSTUM_TOP][0] * viewFrustum[FRUSTUM_TOP][0]) +
                         (viewFrustum[FRUSTUM_TOP][1] * viewFrustum[FRUSTUM_TOP][1]) +
                         (viewFrustum[FRUSTUM_TOP][2] * viewFrustum[FRUSTUM_TOP][2])   );
                         
                      viewFrustum[FRUSTUM_TOP][0] /= norm;
                      viewFrustum[FRUSTUM_TOP][1] /= norm;
                      viewFrustum[FRUSTUM_TOP][2] /= norm;
                      viewFrustum[FRUSTUM_TOP][3] /= norm;
                      
                      //--------------------
                      
                      /* Extract the numbers for the FAR plane */
                      viewFrustum[FRUSTUM_FAR][0] = clip[3]  - clip[2];
                      viewFrustum[FRUSTUM_FAR][1] = clip[7]  - clip[6];
                      viewFrustum[FRUSTUM_FAR][2] = clip[11] - clip[10];
                      viewFrustum[FRUSTUM_FAR][3] = clip[15] - clip[14];
                      
                      /* Normalize the FAR plane */
                      norm = (float)Math.sqrt(
                              (viewFrustum[FRUSTUM_FAR][0] * viewFrustum[FRUSTUM_FAR][0]) +
                         (viewFrustum[FRUSTUM_FAR][1] * viewFrustum[FRUSTUM_FAR][1]) +
                         (viewFrustum[FRUSTUM_FAR][2] * viewFrustum[FRUSTUM_FAR][2])   );
                         
                      viewFrustum[FRUSTUM_FAR][0] /= norm;
                      viewFrustum[FRUSTUM_FAR][1] /= norm;
                      viewFrustum[FRUSTUM_FAR][2] /= norm;
                      viewFrustum[FRUSTUM_FAR][3] /= norm;
                      
                      //--------------------
                      
                      /* Extract the numbers for the NEAR plane */
                      viewFrustum[FRUSTUM_NEAR][0] = clip[3]  + clip[2];
                      viewFrustum[FRUSTUM_NEAR][1] = clip[7]  + clip[6];
                      viewFrustum[FRUSTUM_NEAR][2] = clip[11] + clip[10];
                      viewFrustum[FRUSTUM_NEAR][3] = clip[15] + clip[14];
                      
                      /* Normalize the NEAR plane */
                      norm = (float)Math.sqrt(
                              (viewFrustum[FRUSTUM_NEAR][0] * viewFrustum[FRUSTUM_NEAR][0]) +
                         (viewFrustum[FRUSTUM_NEAR][1] * viewFrustum[FRUSTUM_NEAR][1]) +
                         (viewFrustum[FRUSTUM_NEAR][2] * viewFrustum[FRUSTUM_NEAR][2])   );
                         
                      viewFrustum[FRUSTUM_NEAR][0] /= norm;
                      viewFrustum[FRUSTUM_NEAR][1] /= norm;
                      viewFrustum[FRUSTUM_NEAR][2] /= norm;
                      viewFrustum[FRUSTUM_NEAR][3] /= norm;
                   }


I found a frustum culling tutorial on the web, and it agrees with the code I posted, so apparantly the frustum culling class I have is fine.

I can’t imagine what’s wrong with my program…maybe it has something to do with the fact that I use gluLookAt() for my camera…I’m at total loss.

Well, anyway, no need to bother validating the above code.

Tom,

If you happen to read this, you were right…I was NOT sending the arguments correctly due to a “this.x = x” type error which I totally overlooked in the constructor. That mistake cost me a quite a few hours…

Anway, it works great now, and I’m quite happy about it:)