Ray OBB intersection
2022-08-11
We want to know if a given ray intersects a given oriented bounding box.Let's look first at the most general case in 2D (we'll look at 3D after): A rectangle rotated and translated into world space by matrix mtw.
Our ray has two points in world space: a and b. The center of our box in world space is c. To transform this problem into an Axis-Aligned Bounding Box (AABB) problem: get a and b relative to c, then apply the reverse rotation:
a_rel = a - c;
b_rel = b - c;
a_final = invr * a_rel;
b_final = invr * b_rel;
b_rel = b - c;
a_final = invr * a_rel;
b_final = invr * b_rel;
Where invr is the inverse of the rotation component of mtw.
Our box has width=2 and height=4. But for this algorithm we only care about halfwidth=1 and halfheight=2.
2D Ray AABB intersection in C:
#include <math.h> #define MIN(x, y) ((x) < (y) ? (x) : (y)) #define MAX(x, y) ((x) > (y) ? (x) : (y)) struct FVec2 { float x, y; }; int intersects2d(float halfwidth, float halfheight, struct FVec2 raybase, struct FVec2 raytip) { struct FVec2 r; r.x = raytip.x - raybase.x; r.y = raytip.y - raybase.y; float tmin = -HUGE_VAL, tmax = HUGE_VAL; /* HUGE_VAL constant from math.h */ float t1, t2; t1 = (-halfwidth - base.x) / r.x; t2 = (halfwidth - base.x) / r.x; tmin = MAX(tmin, MIN(t1, t2)); tmax = MIN(tmax, MAX(t1, t2)); t1 = (-halfheight - base.y) / r.y; t2 = (halfheight - base.y) / r.y; tmin = MAX(tmin, MIN(t1, t2)); tmax = MIN(tmax, MAX(t1, t2)); return tmax > MAX(tmin, 0.0f); }So with our data, we can call
intersects2d
like so:intersects2d(halfwidth, halfheight, a_final, b_final);
Because this algorithm operates on each axis individually, we can abstract it to any dimensional space with a for loop over the dimensions.
3D Ray AABB intersection in C:
union FVec3 { struct { float x, y, z; }; float arr[3]; }; int intersects3d(union FVec3 boxScale, union FVec3 raybase, union FVec3 raytip) { union FVec3 r; r.x = raytip.x - raybase.x; r.y = raytip.y - raybase.y; r.z = raytip.z - raybase.z; float tmin = -HUGE_VAL, tmax = HUGE_VAL; float t1, t2; for (int i = 0; i < 3; i++) { t1 = (-boxScale.arr[i] - base.arr[i]) / r.arr[i]; t2 = (boxscale.arr[i] - base.arr[i]) / r.arr[i]; tmin = MAX(tmin, MIN(t1, t2)); tmax = MIN(tmax, MAX(t1, t2)); } return tmax > MAX(tmin, 0.0f); }Using a union here lets us access FVec3 members with x,y,z as well as [0],[1],[2] via "arr".
boxScale is the vector (halfwidth, halfheight, halfdepth) for the 3D AABB.
We can avoid 3 floating point divisions by inverting r and switching the loop divisions to multiplications like so:
int intersects3d(union FVec3 boxScale, union FVec3 raybase, union FVec3 raytip) { union FVec3 r; r.x = 1.0f / (raytip.x - raybase.x); r.y = 1.0f / (raytip.y - raybase.y); r.z = 1.0f / (raytip.z - raybase.z); float tmin = -HUGE_VAL, tmax = HUGE_VAL; float t1, t2; for (int i = 0; i < 3; i++) { t1 = (-boxScale.arr[i] - base.arr[i]) * r.arr[i]; t2 = (boxscale.arr[i] - base.arr[i]) * r.arr[i]; tmin = MAX(tmin, MIN(t1, t2)); tmax = MIN(tmax, MAX(t1, t2)); } return tmax > MAX(tmin, 0.0f); }Credit to Tavianator for the Ray AABB intersection algorithm.