Intersection Math & Algorithms - Learn to Derive

One of the most important group of algorithms inside game engines are intersection algorithms. Even though there are plenty of sites showing code for intersection algorithms, there are only a few describing the math backround behind this, and algorithm derivation. To understand this article you need to know just a bit about linear algebra.

Primitive description

Let's start with a mathematical description of primitives we're going to intersect. For this article I've used just ray (line), plane, sphere, axis-aligned bounding box and triangle ... these should be enough for a basic game engine.

Ray

This is the simplest (and very useful) primitive you can think about. Let us define it with two 4-component numbers, point (further referred as origin) and direction. Mathematically the definition is:
Where:

x represents any point on a line

o represents some exact specified point on line (e.g. origin)

d represents direction vector of line

t is parameter within some specified range

The range specifies whether our line is infinite in both directions (+d and -d), whether it's just in a single direction, or whether it's finite.
Writing a ray object in some programming language is straight-forward of course, you should end up with something like this:
class Ray
{
public:
float4 origin;
float4 direction;
<>
<>
};
Constructor and methods are straight-forward (and should be also inline for performance reasons).

Plane

Another basic primitive. There are more ways to define a plane, but we will derive one that is well known and used. What is a plane? It's a point set that contains every such point in space, that when we create vector from that point to our known point on a plane, that vector has to be perpendicular to plane normal. And we know that perpendicular vectors are those whose dot product is equal to zero. So:
Where:

n is unit vector in the plane normal direction

x is every point on the plane

p is our specified point on the plane

Expanding this yields:
Substitute:
And we get something very familiar:
Which is a plane equation (so we proved that the definition we use is actually a plane). Definition in programming language could be like this:
class Plane
{
public:
float4 point;
float4 normal;
<>
<>
};
A very simple definition. Let's jump ahead.

Sphere

Yet another basic primitive, that is widely used. The definition is surface containing all points in distance from sphere's center less-or-equal to sphere's radius. We will define it using an analytic equation:
Where:

Axis-aligned Box

From here on I'll call this one just AABB (which stands for Axis-Aligned Bounding Box). Definition is very simple, as we work in Cartesian coordinate system, we have perpendicular axes X, Y and Z - AABB is defined as volume between minimum and maximum point in this system, the sides are made by planes orthogonal to planes formed by all 3 combinations of 2 base axis (XY, XZ and YZ). Mathematically we can define it as:
Where:

Triangle

The first and only primitive in this article that is going to be defined by more than 2 values, yet very important. Most of the game worlds are described by triangles, most of the simulation use objects described as triangles, and every more (or less) complex 3D object can be decomposed into triangles. They're awesome; as opposed to other boring N-gons, triangles are never concave!
Let's define triangle with 3 points, then any point on triangle can be described as:
With conditions:
Where:

x is every point in triangle

A, B, C are points defining triangle

a, ss, ? are so called barycentric coordinates (e.g. parameters in the equation above)

A triangle inside of a program could be defined as:
class Triangle
{
public:
float4 p[3];
<>
<>
};

Intersections

So now that we have defined some of the basic primitives, it's time to derive intersection equation and also intersection algorithms between the primitives. As I want to keep the article short (and I don't want to storm you with zillion of equations and exhausting equation solving), I will just show you 4 derivations - Ray-Plane (analytic derivation), Ray-Sphere (geometric derivation), Ray-AABB (tricky derivation) and Ray-Triangle (more complex analytic derivation). You should get an idea of how the intersection equation can be derived and in the end you should be able to derive the rest of them on your own.

Ray-Plane intersection

I will derive Ray-Plane intersection analytically, as this is one of the most simple approaches to the derivation. When we want to derive some intersection algorithm analytically, we should write equations of those 2 objects right away, so (I intentionally used commonly known plane equation, instead of our definition):
Now we're looking for a point that lies on both, the ray and the plane - so we're looking for x. So we first write the first equation in scalar form:
Let's substitute these 3 to plane equation:
It's time to reverse substitution we did when "deriving" plane equation, e.g.:
And transformed to vector form we get:
But we can optimize this a little bit:
Voila, that's what we have been looking for! So basically we need just subtraction, 2 dot products and 1 division. In program this can look like (assuming we want to intersect planes in front of ray origin in direction of ray):
bool IntrRayPlane(const Ray* r, const Plane* p, float* t)
{
float denom = dot(p->normal, r->direction);
// Test whether ray and plane aren't parallel
if(fabsf(dotND) < EPSILON)
return false;
*t = -(dot(p->normal, r->origin - p->point) / denom);
return ((*t) > 0.0f);
}
Almost as simple as the equation. Let's jump ahead, time for spheres!

Ray-Sphere intersection

I could perform analytic derivation like in the Ray-Plane intersection derivation, although Ray-Sphere intersection is just one of the cases that can be easily derived using geometry. Let's start with an image:
We're now looking for 2 distances:
Finding C-O is quite straightforward, it's just simple:
Another thing we need to find is distance along the ray to point X, but as we know ray direction, it's just simple projection of L.
Right now we should look again at the image, we can see 3 important right angle triangle, one formed by {d, OX and OC}, another one {d, r, PX} and last one {d, r, P'X}. We need to know distance PX (respectively P'X, they're equal). So let's use Pythagorean theorem twice:
And our 2 hit distances are:
Time for the code (searching just nearest hit):
bool IntrRaySphere(const Ray* r, const Sphere* s, float* t)
{
float rad2 = s->radius * s->radius;
float4 L = s->center - r->origin;
float tPX = dot(L, r->direction);
if(tPX < 0.0)
return false;
float dsq = dot(L, L) - tPX * tPX;
if(dsq > rad2)
return false;
float thit = sqrt(rad2 - dsq);
*t = tPX - thit;
if(*t < 0.0f)
*t = tPX + thit;
return ((*t) < 0.0f);
}
Just a side note. Analytic derivation in this case is also very trivial, you will end up with quadratic equation, where determinant is negative in case of no hit, zero in case of single point hit (e.g. tangent line), and positive in case of secant.
Let's jump ahead to boxes!

Ray-AABB intersection

Another important one, although a bit tricky. Let's start with an image again:
You can see, that box in 2D is formed by 2 slabs, horizontal and vertical. 3D is analogous to this, but of course it's formed by 3 slabs. For each slab we want to find minimum distance and maximum distance, e.g.:
Where a and b represents minimum (respectively maximum) point of our AABB. What we get is the minimum and maximum distance to each slab. Now we have to correctly order the distances along the ray direction, this is quite simple:
Where min/max is defined as:
Entry/exit point is then defined as maximum of near distances (see the image), thus:
Where hmax/hmin function is horizontal minimum/maximum, defined as:
Of course a hit only occurs when the exit point is larger than zero, and the exit point is further than the entry point (if it isn't, we missed the AABB). Time to code:
bool IntrRayAABB(const Ray* r, const AABB* b, float* enter, float* exit)
{
float4 tmin = (b->minimum - r->origin) / r->direction;
float4 tmax = (b->maximum - r->origin) / r->direction;
float4 tnear = f4min(tmin, tmax);
float4 tfar = f4min(tmin, tmax);
*enter = max(max(tnear.x, 0.0f), max(tnear.y, tnear.z));
*exit = min(tfar.x, min(tfar.y, tfar.z));
return (*exit > 0.0f && *enter < *exit);
}
It can be a little more optimized, although I'll leave that to reader as an exercise.

Conclusion

Intersection algorithms, as I've said, are core algorithms for practically every game engine! And not just game engines, ray-tracers, physics simulators, collision detection libraries, even some AI simulations use them, etc. If you made it here, then I have to say thanks for reading my article, and I hope you've learned something new.
9 Apr 2013: Initial release