Sign in to follow this  
cozzie

Strange frustum problem...

Recommended Posts

Hi all,

When I check a sphere against my frustum (d3d dx9 engine, c++), it's all good.
Now I made a function to check a bounding box against the frustum, but it now does this:

every object is only rendered when it's inside the frustum AND somehow intersects with the frustum planes.

I've double checked the min and max positions of the bounding boxes, they're all ok.

Can someone help me out?

Here's the code:


bool CD3dcam::SphereInFrustum(D3DXVECTOR3* pPosition, float pRadius)
{
float fDistance;
for(int i=0;i<6;++i)
{
fDistance = D3DXPlaneDotCoord(&mFrustumPlane[i], pPosition) + pRadius;

if(fDistance < -pRadius) return false; // outside the frustum
if((float)fabs(fDistance) < pRadius) return true; // intersects
}
return true; // inside the frustum completely or intersecting; see XLS
}

// NO PROBLEMS WITH SHERE CHECKING

bool CD3dcam::BoxInFrustum(D3DXVECTOR3* pMinPosition, D3DXVECTOR3* pMaxPosition)
{
float fDistance;

for(int i=0;i<6;++i)
{
fDistance = D3DXPlaneDotCoord(&mFrustumPlane[i], pMinPosition);
if(fDistance <= 0) return true;

fDistance = D3DXPlaneDotCoord(&mFrustumPlane[i], pMaxPosition);
if(fDistance <= 0) return true;
}
return false;
}


Share this post


Link to post
Share on other sites
hi szecs,
that would sound logical indeed, because inside the frustum the value should allways be 0 or higher.
I tried it out, but now all objects are rendered at all time, even when there not in the frustum (I keep a counter).

Share this post


Link to post
Share on other sites
Just think about it a bit, and you should see what's the problem.

For example if the first condition is not true the second one will surely be: the two conditions should be in AND relation, now they are in OR (think about it)

Share this post


Link to post
Share on other sites
Reviewed your code. Any coordinate in the world will be on the positive side of one of the frustum planes.

Try checking for both min/max < 0 for each plane. That will, at least, cull boxes entirely on the outside of one of the planes.

EDIT:
fdist1 = D3DXPlane..(plane[i],min);
fdist2 = D3DXPlane..(plane[i],max);
if( fdist1 < 0 && fdist2 < 0 ) return false;

If false is not returned for any frustum plane, return true.

EDIT: ninja #2 [smile]

Share this post


Link to post
Share on other sites
Both thanks for the hints, I understand that I should check both (and not or), the reason why I used or is that I also want to render boxen that intersect the frustum.

I changed the code now to this:


bool CD3dcam::BoxInFrustum(D3DXVECTOR3* pMinPosition, D3DXVECTOR3* pMaxPosition)
{
float fDistanceMin, fDistanceMax;

for(int i=0;i<6;i++)
{
fDistanceMin = D3DXPlaneDotCoord(&mFrustumPlane[i], pMinPosition);
fDistanceMax = D3DXPlaneDotCoord(&mFrustumPlane[i], pMaxPosition);
if(fDistanceMin < 0 && fDistanceMax < 0) return false;
}
return true;
}



This works almost, only now objects that are not fully in the frustum are not rendered I think. When I change it to '<= 0' instead of "<0" still objects that are partially in the frustum are not returned as 'true' and therefor not rendered.

Share this post


Link to post
Share on other sites
Because you're using AABB, it's possible that min and max could be outside the frustum, but one of the corners (not min or max) could be inside the frustum.

I think you'll have to test all 8 corners of the AABB.

Share this post


Link to post
Share on other sites
Ok, I can make that work and code that.
But will checking all 8 points still be be quicker then just checking all objects (included boxes) with a radius as a bounding sphere?

Share this post


Link to post
Share on other sites
Quote:
But will checking all 8 points still be be quicker then just checking all objects (included boxes) with a radius as a bounding sphere?

Unfortunately, no. However, depending on how many objects and what their shapes are, you can cull bounding spheres that are outside the frustum. Then do the full 8 point AABB check only for those objects whose bounding spheres are not outside the frustum.

EDIT: I don't know how you determine the bounding radius, but you can precalculate the radius (and sphere center position) for each object if you don't already. That would speed things up a bit as you wouldn't have to calculate the radius for each "in-frustum" call.

Share this post


Link to post
Share on other sites
Thanks, knowing this I would suggest the following approach:

One time loading/initializing:

- calculate radius for full mesh/object
- calculate radius for subobjects
- calculate 8 AABB corners for subobjects

RenderFrame:

- update camera
- occlusioncull
- if full mesh/object visible:
- for each sububject: check if sphere's in frustum
- for each subobject: check against 8x AABB box
- if both true, then render

OcclusionCull:

- check if full mesh/object is in frustum, using radius and sphere in frustum
if so, full mesh visible = true

Would this be a good approach?

I also considered keeping track per full and sub-object if it has to be checked using boundingbox or sphere, but I doubt if that will help.

Share this post


Link to post
Share on other sites
Quote:
- if full mesh/object visible:

If, by "full" you mean some portion is visible, that looks like a good approach. Certainly precalculating as many parameters as possible will give benefits.

As you obviously realize, the fastest draw for a triangle is one that's not drawn at all.

Take note, cozzie, that, if you're concerned about performance, you might want to do some profiling to make sure your algorithm is, in fact, showing some benefit under actual game conditions.

Quote:
I also considered keeping track per full and sub-object if it has to be checked using boundingbox or sphere, but I doubt if that will help.

Probably of less benefit than the frustum culling you're already going to do. But, at the expense of a single bool variable per object (if it can be precalculated) and a single "should-I-check-AABB?" statement, there may be a marginal benefit.

Share this post


Link to post
Share on other sites
Hi
I implemented the 8x AABB boundingbox check, not very clean code yet, but first to see if it works.

Here it is:


// one time per subobject

mAttrBoundingBox[obj].boxArray[0].x = mAttrBoundingBox[obj].min.x;
mAttrBoundingBox[obj].boxArray[0].y = mAttrBoundingBox[obj].min.y;
mAttrBoundingBox[obj].boxArray[0].z = mAttrBoundingBox[obj].min.z;

mAttrBoundingBox[obj].boxArray[1].x = mAttrBoundingBox[obj].max.x;
mAttrBoundingBox[obj].boxArray[1].y = mAttrBoundingBox[obj].min.y;
mAttrBoundingBox[obj].boxArray[1].z = mAttrBoundingBox[obj].min.z;

mAttrBoundingBox[obj].boxArray[2].x = mAttrBoundingBox[obj].max.x;
mAttrBoundingBox[obj].boxArray[2].y = mAttrBoundingBox[obj].max.y;
mAttrBoundingBox[obj].boxArray[2].z = mAttrBoundingBox[obj].min.z;

mAttrBoundingBox[obj].boxArray[3].x = mAttrBoundingBox[obj].min.x;
mAttrBoundingBox[obj].boxArray[3].y = mAttrBoundingBox[obj].max.y;
mAttrBoundingBox[obj].boxArray[3].z = mAttrBoundingBox[obj].min.z;

mAttrBoundingBox[obj].boxArray[4].x = mAttrBoundingBox[obj].min.x;
mAttrBoundingBox[obj].boxArray[4].y = mAttrBoundingBox[obj].min.y;
mAttrBoundingBox[obj].boxArray[4].z = mAttrBoundingBox[obj].max.z;

mAttrBoundingBox[obj].boxArray[5].x = mAttrBoundingBox[obj].max.x;
mAttrBoundingBox[obj].boxArray[5].y = mAttrBoundingBox[obj].min.y;
mAttrBoundingBox[obj].boxArray[5].z = mAttrBoundingBox[obj].max.z;

mAttrBoundingBox[obj].boxArray[6].x = mAttrBoundingBox[obj].max.x;
mAttrBoundingBox[obj].boxArray[6].y = mAttrBoundingBox[obj].max.y;
mAttrBoundingBox[obj].boxArray[6].z = mAttrBoundingBox[obj].max.z;

mAttrBoundingBox[obj].boxArray[7].x = mAttrBoundingBox[obj].min.x;
mAttrBoundingBox[obj].boxArray[7].y = mAttrBoundingBox[obj].max.y;
mAttrBoundingBox[obj].boxArray[7].z = mAttrBoundingBox[obj].max.z;


// check function

bool CD3dcam::BoxInFrustum(BOUNDINGBOX *pBoundingBox)
{
float fDistance;

for(int i=0;i<6;++i)
{
for(int bc=0;bc<8;++bc)
{
fDistance = D3DXPlaneDotCoord(&mFrustumPlane[i], &pBoundingBox->boxArray[bc]);
if(fDistance >= 0) return true;
}
}
return false;
}


Unfortunately everything is still rendered/ returned as true.
Probably has something to do with return true to early, although I don't understand. Will try some things.

If you see something that's wrong, please let me know.

Share this post


Link to post
Share on other sites
You don't have to check all 8 corners of the AABB. Google 'aabb plane test', 'box plane test', 'aabb frustum culling', or similar terms, and you should find some example implementations.

In particular, look for references that talk about the 'n-vertex' and the 'p-vertex' (I think that's the terminology that's often used). This actually still isn't the fastest method available (I don't believe), but it seems to be better documented than the alternatives.

Share this post


Link to post
Share on other sites
Good point :)

Solved it, I was looking for something difficult, but it was something stupid.

I did this: BoxInFrustum(&mD3dMeshes[oc].mAttrBoundingBox) which had to be
BoxInFrustum(&mD3dMeshes[oc].mAttrBoundingBox[obj]).
Stupid.

Got it all working know, that's for the help.
Next thing I'll do is do a much larger testscene and see if it's quicker or nog to keep track of 'check 8x AABB' or not.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this