The Precision Edge: OBB SAT Test Using Pre-Scaled Basis Vectors
In the high-performance landscape of 2026 physics engines, collision detection efficiency is measured in nanoseconds. The Separating Axis Theorem (SAT) remains the industry standard for detecting intersections between convex shapes, specifically Oriented Bounding Boxes (OBB). However, the traditional approach involves frequent normalization and scalar-vector multiplications that can bog down the CPU. By utilizing pre-scaled basis vectors—where the box's orientation axes already incorporate the half-extents—we can eliminate multiple square root operations and divisions during the overlap test. This tutorial explores the mathematical transition from unit-vector SAT to pre-scaled SAT, providing a streamlined path for modern game physics implementation.
Table of Content
- Purpose: Reducing Arithmetic Intensity
- The Logic: Dot Products and Scaled Projections
- Step-by-Step: Implementing the Pre-Scaled Test
- Use Case: Destructible Environmental Props
- Best Results: Early Outs and SIMD Optimization
- FAQ
- Disclaimer
Purpose
Traditional SAT requires 15 axis tests for two OBBs (3 face normals of A, 3 face normals of B, and 9 cross products). Using pre-scaled basis vectors serves to:
- Eliminate Half-Extent Lookups: By baking the box dimensions into the basis vectors, you reduce memory fetches.
- Simplify Projection Math: The projection radius of a box onto an axis becomes a simple sum of absolute dot products without explicit multiplication by extents.
- Improve Cache Locality: Storing scaled axes as a single matrix-like structure speeds up the iterative testing process.
The Logic: Dot Products and Scaled Projections
Standard SAT defines an OBB by a center $C$, unit axes $u_x, u_y, u_z$, and half-extents $e_x, e_y, e_z$. The "Pre-Scaled" approach replaces these with three vectors:
$A_0 = u_x \times e_x$, $A_1 = u_y \times e_y$, and $A_2 = u_z \times e_z$.
When projecting Box A onto an axis $L$, the radius $R_a$ is simply:
$R_a = |A_0 \cdot L| + |A_1 \cdot L| + |A_2 \cdot L|$.
This removes the need to store or multiply by $e_i$ during the 15-axis loop, as the magnitude of the axis itself carries the "size" of the box.
Step-by-Step
1. Data Structure Setup
Define your OBB using the center and the pre-scaled axes. Ensure these axes are updated only when the object's transform or scale changes.
struct ScaledOBB {
Vector3 center;
Vector3 axes[3]; // Pre-multiplied: axis halfExtent
};
2. Generate the Rotation Matrix
To test Box A against Box B, you need the relative orientation. Calculate the dot products between the unit versions of the axes to build a $3 \times 3$ rotation matrix $R$, where $R_{ij} = uA_i \cdot uB_j$.
3. Test the 6 Face Normals
Test the axes of Box A and Box B. For each axis $L$:
- Calculate the projection radius $R_a$ and $R_b$.
- Calculate the distance between centers $D = |(CenterB - CenterA) \cdot L|$.
- If $D > R_a + R_b$, return False (No Collision).
4. Test the 9 Cross Products
Test the axes formed by $L = A_i \times B_j$. Note: With pre-scaled vectors, you must be careful with the magnitude of $L$. If the cross product results in a near-zero vector (parallel axes), skip that specific test to avoid floating-point errors.
Use Case
A developer is creating a high-speed racing game where hundreds of debris pieces (OBB colliders) are scattered across the track.
- The Action: The physics engine performs thousands of narrow-phase checks per frame.
- The Implementation: By using pre-scaled basis vectors, the developer reduces the per-test instruction count by approximately 20%.
- The Result: The game maintains a stable 144 FPS even during multi-car pileups, as the SAT overhead is minimized.
Best Results
| Optimization | Traditional SAT | Pre-Scaled SAT |
|---|---|---|
| Arithmetic | Extent multiplications in loop | Baked into basis |
| Normalization | Required for axis logic | Deferred or minimized |
| Cross Products | Standard $u \times v$ | Scaled $A \times B$ (Watch precision) |
| SIMD Friendly | Moderate | High (Vectorized Dot Products) |
FAQ
Does this handle non-uniform scaling?
Yes. Because the half-extents are baked into the basis vectors, non-uniform scaling of the GameObject is naturally represented by the length of $A_0, A_1,$ and $A_2$.
Why skip the cross product if axes are parallel?
If two axes are parallel, their cross product is a zero vector. Attempting to project onto a zero vector will result in $D=0, R=0$, which fails the separation test and can lead to "false positives" in collision detection.
Is this faster than AABB?
No. AABB (Axis-Aligned Bounding Box) is always faster because it only tests 3 fixed axes. OBB SAT is used only when objects are rotated and require tighter collision bounds than an AABB can provide.
Disclaimer
Pre-scaling basis vectors can lead to very large or very small values in the cross-product phase if the boxes have extreme size differences. This can cause floating-point precision issues (catastrophic cancellation). Always implement an epsilon check when computing cross-product axes. This tutorial assumes a standard right-handed coordinate system. March 2026.
Tags: CollisionDetection, PhysicsEngine, SATAlgorithm, OBBColliders