Ray-OBB Intersection Using the Slab Method in GLSL

This article demonstrates how to implement ray intersections against an Oriented Bounding Box (OBB) in GLSL using the slab method. By transforming the incoming ray into the box's local coordinate space, the problem simplifies into a standard Axis-Aligned Bounding Box (AABB) slab test, allowing efficient computation directly within vertex, fragment, or compute shaders.

Concept Overview

An OBB is defined by a center position, half-extents (size along each local axis), and an orientation represented by an orthonormal \(3 \times 3\) rotation matrix (or three local basis axes).

The slab method tests a ray against three pairs of parallel planes defined by the bounding box faces. Because checking an arbitrary orientation in world space requires projecting the ray along each axis, the most efficient approach in GLSL is to transform the ray's origin and direction into the OBB's local coordinate space. In local space, the box is an AABB centered at the origin with bounds from -extents to +extents.

GLSL Implementation

Below is a complete, self-contained GLSL function to perform the intersection test.

struct Ray {
    vec3 origin;
    vec3 direction;
};

struct OBB {
    vec3 center;
    vec3 extents;      // Half-widths along each local axis
    mat3 orientation;  // Columns are the orthonormal local axes (X, Y, Z)
};

// Returns true if the ray intersects the OBB.
// tNear and tFar output the entry and exit intersection distances.
bool intersectRayOBB(Ray ray, OBB obb, out float tNear, out float tFar) {
    // 1. Transform ray to OBB local space
    // Since orientation is orthonormal, transpose is equivalent to inverse.
    mat3 invRotation = transpose(obb.orientation);
    
    vec3 localOrigin = invRotation * (ray.origin - obb.center);
    vec3 localDir    = invRotation * ray.direction;

    // 2. Compute intersection distances for all three slabs
    vec3 invDir = 1.0 / localDir;
    vec3 t0 = (-obb.extents - localOrigin) * invDir;
    vec3 t1 = ( obb.extents - localOrigin) * invDir;

    // Handle negative direction components
    vec3 tMinVec = min(t0, t1);
    vec3 tMaxVec = max(t0, t1);

    // 3. Find largest entry t and smallest exit t
    tNear = max(max(tMinVec.x, tMinVec.y), tMinVec.z);
    tFar  = min(min(tMaxVec.x, tMaxVec.y), tMaxVec.z);

    // 4. Validate intersection
    // The ray hits if entry occurs before exit and the box is not entirely behind the ray
    return (tNear <= tFar) && (tFar >= 0.0);
}

Step-by-Step Breakdown

1. Ray Transformation

The OBB's orientation matrix consists of three normalized, mutually perpendicular vectors representing its local \(X\), \(Y\), and \(Z\) axes. Because this matrix is orthogonal, its inverse equals its transpose (transpose(obb.orientation)). Multiplying the centered ray origin and direction by this transposed matrix rotates the ray into the OBB's local coordinate system.

2. Computing Slab Intervals

Once in local space, the box boundaries are defined by \([-extents, +extents]\). For each axis:

  • Calculate the distance from the origin to the near and far slab planes.
  • Using 1.0 / localDir leverages vectorization and avoids multiple separate division operations. In GLSL, division by zero produces +infinity or -infinity, which works consistently with min and max under IEEE 754 floating-point standards.

3. Interval Reduction

min(t0, t1) and max(t0, t1) ensure that tMinVec contains the entry distances and tMaxVec contains the exit distances regardless of ray direction. The overall ray entry distance tNear is the maximum of all three entry distances, while the exit distance tFar is the minimum of all three exit distances.

4. Hit Evaluation

An intersection occurs if:

  • tNear <= tFar: The ray overlaps the intersection of all three slabs simultaneously.
  • tFar >= 0.0: The intersection volume is in front of the ray's origin, not behind it. If tNear < 0.0, the ray origin is located inside the box.