🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

How can I fix this bug affecting my AABB function?

Started by
17 comments, last by Dirk Gregorius 5 years, 10 months ago
43 minutes ago, Doug Patterson said:

Why not have nested conditionals so that if the collision check along the x-axis fails you can abandon the checks on y and z and return a false.

The compiler should be able to figure this option out. But probably the fastest way would be to use simd instructions to do all 3 dimensions at once. (Which the compiler might do for us under the hood as well - aligning data helps here i guess.)

8 minutes ago, bmarci said:

if ((abs(a.pos.x - b.pos.x) < a.size.x + b.size.x) && abs(a.pos.y - b.pos.y) < a.size.y + b.size.y) && abs(a.pos.z - b.pos.z) < a.size.z + b.size.z)) { ... you have collision ... }

Notice this code assumes pos at the center not at the corner, so it differs from the OP convention.

Usually the fastest representation of boxes is having min and max coords, which has the same memory but does not need additional multiplications or additions when checking overlaps (or also for ray hit):

return (a.max >= b.min && a.min <= b.max);

Advertisement
49 minutes ago, JoeJ said:

Usually the fastest representation of boxes is having min and max coords, which has the same memory but does not need additional multiplications or additions when checking overlaps (or also for ray hit):

This is true for overlap tests. For distance between AABB and ray vs AABB the center and half-extend representation requires the least number of instructions iirc. To be honest I doubt it really matters as you can go back and forth between the two trivially and I would expect no clear winner for performance. It is really more theoretically. 

You test two AABB for overlap using the separating axis test (SAT). You can look at this in three ways:

1) You make one box stationary (e.g. A) and then check if B is either to the left or right or above or below A. If non of these tests returns true then the boxes must be overlapping. This is what JoeJ suggested.

2) The other way to look at this is to project the boxes onto each axis (which is trivial with your center and half-extend representation - assuming your 'size' is the half-extend) and then test overlap of the projected intervals. In practice this is really just a 1D sphere overlap test if you like to think of an interval as a 1D sphere (e.g. a center with a radius).  

3) You solve the overlap test in Minkowski space. E.g. you add the half-extend of B to A and then just test the center of B for containment in the inflated box A.

You can also compute the distance between the AABB and if the distance is zero the boxes must overlap. All these tests are equivalent and I doubt any of them will make a noticeable performance difference. 

Let me know if you need example code for the three approaches.

HTH,

-Dirk

40 minutes ago, Dirk Gregorius said:

For distance between AABB and ray vs AABB the center and half-extend representation requires the least number of instructions iirc.

I remember otherwise, but i'm not so sure myself (computers became so fast it won't matter :) ).

Anyways, quickly tried based on my simd code:


	    void DistanceRayAABoxFrontAndBackface_MinMaxMethod (qScalar &ffd, qScalar& bfd, const Ray& ray, const AABox& box)
    {
        qVec3 t0 = qVec3(box.minmax[0] - ray.origin).MulPerElem (ray.invDirection);
        qVec3 t1 = qVec3(box.minmax[1] - ray.origin).MulPerElem (ray.invDirection);
	        qVec3 tMin = t0.MinPerElem (t1);
        qVec3 tMax = t0.MaxPerElem (t1);
        ffd = tMin.MaxElem(); // front face distance (behind origin if inside box) 
        bfd = tMax.MinElem(); // back face distance    
    }
	    /*void DistanceRayAABoxFrontAndBackface_CenterMethod_Untested (qScalar &ffd, qScalar& bfd, const Ray& ray, const AABox& box)
    {
        qVec3 t = qVec3(box.center - ray.origin).MulPerElem (ray.invDirection);
        qVec3 ext = box.extend.MulPerElem (ray.invDirection);
        qVec3 t0 = t - ext;
        qVec3 t1 = t + ext;
	        qVec3 tMin = t0.MinPerElem (t1);
        qVec3 tMax = t0.MaxPerElem (t1);
        ffd = tMin.MaxElem(); // front face distance (behind origin if inside box) 
        bfd = tMax.MinElem(); // back face distance    
    }*/
	

Center requires one more addition (also more registers?) it seems. But if anyone knows there's a better way please let me know, because having the center available would be nice for most applications.

I meant distance between two AABB and boolean overlap test between a ray and AABB. What is this function doing? Does it compute the fractions where the ray enters and leaves the AABB?

 

17 minutes ago, Dirk Gregorius said:

overlap test between a ray and AABB

Ah ok, my function calculates the exact intersections of ray against box.

I see, this is an implementation of the slab test. I used this for a bit, but then switched to a boolean SAT test for raycasts since the inverse ray direction is not well defined for rays along the principal axes (e.g. down) as you get division by zero (e.g. some components of your ray.invDirection with be INF) and I never had use for the intersection fractions anyway.

There was a presentation to make the implementation of this test work in SIMD with division by zeros (INF) which is actually not trivial! You can find it here:

http://schedule.gdconf.com/session/extreme-simd-optimized-collision-detection-in-titanfall/851833

If you see how tedious it is to make this function work *robustly* you might come to the same conclusion as me that it is simply the wrong algorithm. I found it funny to see the author jump through so many hoops to make this working where better alternatives exist. Funnily this implementation of ray vs AABB is very popular (I think in the ray tracing community), but in my opinion a bad choice.

Yeah, personally i got away with jittering the ray like so:

#define FP_EPSILON 1.0e-6f
#define FP_EPSILON2 FP_EPSILON*FP_EPSILON

void Ray::Jitter () 
    {
        for (int i=0; i<3; i++)
        {
            if (fabs(direction) < FP_EPSILON2) direction = FP_EPSILON2;
        }
    }

Then the inverse was stable enough for my graphics purposes where robustness was no issue.

But i get your point, actually having some fun debugging crossfield aligned motorcycle graph tracing on mesh surface... even a 2D line intersection can give a lot of headache :D

 

Another numerical headache is hair rendering. Lots of sliver'ish triangles and the rays can come in at a very shallow angle. A good robustness test I use for physics is a simple raytracer. E.g. I draw gray flat shaded triangles in front of a bright green background. Then I move the ray origin a million units or so back. You can see the green pixels nicely shine through this way if there are numerical issues.

This topic is closed to new replies.

Advertisement