Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

Is there a known 'most efficient' algorithm for AABB vs Ray collision detection?

I recently stumbled accross Arvo's AABB vs Sphere collision algorithm, and I am wondering if there is a similarly noteworthy algorithm for this.

One must have condition for this algorithm is that I need to have the option of querying the result for the distance from the ray's origin to the point of collision. having said this, if there is another, faster algorithm which does not return distance, then in addition to posting one that does, also posting that algorithm would be very helpful indeed.

Please also state what the function's return argument is, and how you use it to return distance or a 'no-collision' case. For example, does it have an out parameter for the distance as well as a bool return value? or does it simply return a float with the distance, vs a value of -1 for no collision?

(For those that don't know: AABB = Axis Aligned Bounding Box)

share|improve this question
    
I might be wrong but I think you will still get false positives with this algorithm. You are right that if all corners are on same side when checking the 3 axis, there is no collision. But it seems like you can still have the condition where all 3 axis have points on both sides and still have no collision. I generally check to see if entry/exit distances overlap on all three slabs to know for sure. It's from the Geometric tools site. –  Steve H Oct 12 '11 at 15:29
    
Why the must have condition for distance query? If there is an even faster algorithm for the case when you do not need the distance, don't you want to know about it, too? –  Sam Hocevar Dec 9 '11 at 12:17
    
well, no, not really. I need to know at what distance the collision happens. –  Asher Einhorn Dec 9 '11 at 12:36
    
actually I suppose you're right, I'll edit the question. –  Asher Einhorn Dec 9 '11 at 12:56
3  
As I posted in your other thread, there's a good resource for these types of algorithms here: realtimerendering.com/intersections.html –  Tetrad Dec 9 '11 at 17:03
show 6 more comments

7 Answers 7

up vote 10 down vote accepted

Andrew Woo, who along with John Amanatides developed the raymarching algorithm (DDA) used ubiquitously in raytracers, wrote this algorithm. Rather than being built specifically for integration through a grid (eg. a voxel volume) as DDA is (see zacharmarz' answer), this algorithm is specifically suited to worlds that are not evenly subdivided, such as your typical polyhedra world found in most 3D games.

The approach provides support for 3D, and optionally does backface culling. The algorithm is derived from the same principles of integration used in DDAs, so it is very quick. More detail can be found in the original Graphics Gems volume (1990).

Many other approaches specifically for Ray-AABB to be found at realtimerendering.com.

EDIT: An alternative, branchless approach -- which would be desirable on both GPU & CPU -- may be found here.

share|improve this answer
    
ah! you beat me to it, I just came across it this morning. Great find! –  Asher Einhorn Dec 14 '11 at 9:46
    
Pleasure, Sir. I'd also suggest comparing any algorithms you find on this sort of basis. (There are more official lists like this elsewhere, but can't find any right now.) –  Nick Wiggill Dec 14 '11 at 11:12
    
The article is here –  bobobobo Oct 13 '12 at 13:45
    
A well commented implementation of Woo's algorithm may be found here. –  Nick Wiggill Dec 23 '12 at 14:03
add comment

Nobody described the algorithm here, but the Graphics Gems algorithm is simply:

  1. Using your ray's direction vector, determine which 3 of the 6 candidate planes would be hit first. If your (unnormalized) ray direction vector is (-1, 1, -1), then the 3 planes that are possible to be hit are +x, -y, and +z.

  2. Of the 3 candidate planes, do find the t-value for the intersection for each. Accept the plane that gets the largest t value as being the plane that got hit, and check that the hit is within the box. The diagram in the text makes this clear:

enter image description here

My implementation:

bool AABB::intersects( const Ray& ray )
{
  // EZ cases: if the ray starts inside the box, or ends inside
  // the box, then it definitely hits the box.
  // I'm using this code for ray tracing with an octree,
  // so I needed rays that start and end within an
  // octree node to COUNT as hits.
  // You could modify this test to (ray starts inside and ends outside)
  // to qualify as a hit if you wanted to NOT count totally internal rays
  if( containsIn( ray.startPos ) || containsIn( ray.getEndPoint() ) )
    return true ; 

  // the algorithm says, find 3 t's,
  Vector t ;

  // LARGEST t is the only one we need to test if it's on the face.
  for( int i = 0 ; i < 3 ; i++ )
  {
    if( ray.direction.e[i] > 0 ) // CULL BACK FACE
      t.e[i] = ( min.e[i] - ray.startPos.e[i] ) / ray.direction.e[i] ;
    else
      t.e[i] = ( max.e[i] - ray.startPos.e[i] ) / ray.direction.e[i] ;
  }

  int mi = t.maxIndex() ;
  if( BetweenIn( t.e[mi], 0, ray.length ) )
  {
    Vector pt = ray.at( t.e[mi] ) ;

    // check it's in the box in other 2 dimensions
    int o1 = ( mi + 1 ) % 3 ; // i=0: o1=1, o2=2, i=1: o1=2,o2=0 etc.
    int o2 = ( mi + 2 ) % 3 ;

    return BetweenIn( pt.e[o1], min.e[o1], max.e[o1] ) &&
           BetweenIn( pt.e[o2], min.e[o2], max.e[o2] ) ;
  }

  return false ; // the ray did not hit the box.
}
share|improve this answer
    
+1 for actually explaining it (that too with a picture :) –  legends2k Mar 20 at 11:18
add comment

This is my 3D ray / AABox intersection I've been using:

bool intersectRayAABox2(const Ray &ray, const Box &box, int& tnear, int& tfar)
{
    Vector3d T_1, T_2; // vectors to hold the T-values for every direction
    double t_near = -DBL_MAX; // maximums defined in float.h
    double t_far = DBL_MAX;

    for (int i = 0; i < 3; i++){ //we test slabs in every direction
        if (ray.direction[i] == 0){ // ray parallel to planes in this direction
            if ((ray.origin[i] < box.min[i]) || (ray.origin[i] > box.max[i])) {
                return false; // parallel AND outside box : no intersection possible
            }
        } else { // ray not parallel to planes in this direction
            T_1[i] = (box.min[i] - ray.origin[i]) / ray.direction[i];
            T_2[i] = (box.max[i] - ray.origin[i]) / ray.direction[i];

            if(T_1[i] > T_2[i]){ // we want T_1 to hold values for intersection with near plane
                swap(T_1,T_2);
            }
            if (T_1[i] > t_near){
                t_near = T_1[i];
            }
            if (T_2[i] < t_far){
                t_far = T_2[i];
            }
            if( (t_near > t_far) || (t_far < 0) ){
                return false;
            }
        }
    }
    tnear = t_near; tfar = t_far; // put return values in place
    return true; // if we made it here, there was an intersection - YAY
}
share|improve this answer
add comment

Here's the Line vs AABB code I've been using:

namespace {
    //Helper function for Line/AABB test.  Tests collision on a single dimension
    //Param:    Start of line, Direction/length of line,
    //          Min value of AABB on plane, Max value of AABB on plane
    //          Enter and Exit "timestamps" of intersection (OUT)
    //Return:   True if there is overlap between Line and AABB, False otherwise
    //Note:     Enter and Exit are used for calculations and are only updated in case of intersection
    bool Line_AABB_1d(float start, float dir, float min, float max, float& enter, float& exit)
    {
        //If the line segment is more of a point, just check if it's within the segment
        if(fabs(dir) < 1.0E-8)
            return (start >= min && start <= max);

        //Find if the lines overlap
        float   ooDir = 1.0f / dir;
        float   t0 = (min - start) * ooDir;
        float   t1 = (max - start) * ooDir;

        //Make sure t0 is the "first" of the intersections
        if(t0 > t1)
            Math::Swap(t0, t1);

        //Check if intervals are disjoint
        if(t0 > exit || t1 < enter)
            return false;

        //Reduce interval based on intersection
        if(t0 > enter)
            enter = t0;
        if(t1 < exit)
            exit = t1;

        return true;
    }
}

//Check collision between a line segment and an AABB
//Param:    Start point of line segement, End point of line segment,
//          One corner of AABB, opposite corner of AABB,
//          Location where line hits the AABB (OUT)
//Return:   True if a collision occurs, False otherwise
//Note:     If no collision occurs, OUT param is not reassigned and is not considered useable
bool CollisionDetection::Line_AABB(const Vector3D& s, const Vector3D& e, const Vector3D& min, const Vector3D& max, Vector3D& hitPoint)
{
    float       enter = 0.0f;
    float       exit = 1.0f;
    Vector3D    dir = e - s;

    //Check each dimension of Line/AABB for intersection
    if(!Line_AABB_1d(s.x, dir.x, min.x, max.x, enter, exit))
        return false;
    if(!Line_AABB_1d(s.y, dir.y, min.y, max.y, enter, exit))
        return false;
    if(!Line_AABB_1d(s.z, dir.z, min.z, max.z, enter, exit))
        return false;

    //If there is intersection on all dimensions, report that point
    hitPoint = s + dir * enter;
    return true;
}
share|improve this answer
add comment

One thing you might want to investigate is rasterizing the front and backfaces of your bounding box in two seperate buffers. Render the x,y,z values as rgb (this works best for a bounding box with one corner at (0,0,0) and the opposite at (1,1,1).

Obviously, this has limited use but I found it great for rendering simple volumes.

For more detail and code:

http://www.daimi.au.dk/~trier/?page_id=98

share|improve this answer
add comment

This seems similar to the code posted by zacharmarz.
I got this code from the book 'Real-Time Collision Detection' by Christer Ericson under the section '5.3.3 Intersecting Ray or Segment Against Box'

// Where your AABB is defined by left, right, top, bottom

// The direction of the ray
var dx:Number = point2.x - point1.x;
var dy:Number = point2.y - point1.y;

var min:Number = 0;
var max:Number = 1;

var t0:Number;
var t1:Number;

// Left and right sides.
// - If the line is parallel to the y axis.
if(dx == 0){
    if(point1.x < left || point1.x > right) return false;
}
// - Make sure t0 holds the smaller value by checking the direction of the line.
else{
    if(dx > 0){
        t0 = (left - point1.x)/dx;
        t1 = (right - point1.x)/dx;
    }
    else{
        t1 = (left - point1.x)/dx;
        t0 = (right - point1.x)/dx;
    }

    if(t0 > min) min = t0;
    if(t1 < max) max = t1;
    if(min > max || max < 0) return false;
}

// The top and bottom side.
// - If the line is parallel to the x axis.
if(dy == 0){
    if(point1.y < top || point1.y > bottom) return false;
}
// - Make sure t0 holds the smaller value by checking the direction of the line.
else{
    if(dy > 0){
        t0 = (top - point1.y)/dy;
        t1 = (bottom - point1.y)/dy;
    }
    else{
        t1 = (top - point1.y)/dy;
        t0 = (bottom - point1.y)/dy;
    }

    if(t0 > min) min = t0;
    if(t1 < max) max = t1;
    if(min > max || max < 0) return false;
}

// The point of intersection
ix = point1.x + dx * min;
iy = point1.y + dy * min;
return true;
share|improve this answer
    
this is 2d, yes? –  Asher Einhorn Dec 9 '11 at 12:41
    
This is only 2D, yes. Also, the code does is not as well thought as zacharmarz's, which takes care of reducing the number of divisions and tests. –  Sam Hocevar Dec 9 '11 at 14:09
add comment

What I have been using earlier in my raytracer:

// r.dir is unit direction vector of ray
dirfrac.x = 1.0f / r.dir.x;
dirfrac.y = 1.0f / r.dir.y;
dirfrac.z = 1.0f / r.dir.z;
// lb is the corner of AABB with minimal coordinates - left bottom, rt is maximal corner
// r.org is origin of ray
float t1 = (lb.x - r.org.x)*dirfrac.x;
float t2 = (rt.x - r.org.x)*dirfrac.x;
float t3 = (lb.y - r.org.y)*dirfrac.y;
float t4 = (rt.y - r.org.y)*dirfrac.y;
float t5 = (lb.z - r.org.z)*dirfrac.z;
float t6 = (rt.z - r.org.z)*dirfrac.z;

float tmin = max(max(min(t1, t2), min(t3, t4)), min(t5, t6));
float tmax = min(min(max(t1, t2), max(t3, t4)), max(t5, t6));

// if tmax < 0, ray (line) is intersecting AABB, but whole AABB is behing us
if (tmax < 0)
{
    t = tmax;
    return false;
}

// if tmin > tmax, ray doesn't intersect AABB
if (tmin > tmax)
{
    t = tmax;
    return false;
}

t = tmin;
return true;

If this returns true, it's intersecting, if it returns false, it's not intersecting.

If you use the same ray many times, you can precompute dirfrac (only division in whole intersection test). And then it's really fast. And you have also length of ray until intersection (stored in t).

share|improve this answer
    
would it be possible to provide a key for what your variable names mean? –  Asher Einhorn Oct 14 '11 at 8:48
    
I tried to add some explanation in comments. So: "r" is ray, "r.dir" is its unit direction vector, "r.org" is origin, from which you shoot ray, "dirfrac" is just optimization, because you can use it always for the same ray (you don't have to do division) and it means 1 / r.dir. Then "lb" is corner of AABB with all 3 coordinates minimal and "rb" is oposite - corner with maximum coordinates. Output parametr "t" is length of vector from origin to intersection. –  zacharmarz Oct 14 '11 at 9:28
    
what does the function definition look like? Is it possible to find out the distance that the collision occurred on the ray? –  Asher Einhorn Dec 9 '11 at 11:16
    
function returns distance ('t' is distance). And function definition can be whatever you want ;) You should pass ray, lb and rt. –  zacharmarz Dec 9 '11 at 12:01
    
it seems to me like the return type for this code would be a bool? –  Asher Einhorn Dec 9 '11 at 12:37
show 24 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.