The atom of ray tracing: does this ray hit this triangle, and where? Möller–Trumbore solves ray origin + t·direction = a point on the triangle in one shot — no stored plane equation — and hands back the distance t and the barycentric coordinates (u,v) together. Down the center, data flows: the ray and triangle go in, the engine solves, the hit comes out. The blue team builds and defends it; the red team tries to break it.
source Tomas Möller & Ben Trumbore, Fast, Minimum Storage Ray/Triangle Intersection, J. Graphics Tools 2(1):21–28 (1997) — doi:10.1080/10867651.1997.10487468. Rendered, not quoted.
The ray equation O + t·D is set equal to a point written in the triangle's own frame, v0 + u·e1 + v·e2. That is a 3×3 linear system; Cramer's rule solves it with two cross products:
e1=v1−v0, e2=v2−v0, h=D×e2, det a=e1·h. Then u=(O−v0)·h / a, v=D·(s×e1) / a, t=e2·(s×e1) / a. Hit iff u≥0, v≥0, u+v≤1, t>0.
Live values for the current ray:
| quantity | value | test |
|---|
The (u,v) Möller–Trumbore returns are the barycentric coordinates — the same weights that the barycentric sphere interpolates across a triangle's face.
Here they are solved from a ray instead of from a screen pixel: this is the inner loop of ray tracing, the eye-side alternative to the rasterization, which finds the same weights with edge-function signs. Each sphere is the next one's premise.
The blue team's live check: re-run the intersector over the fixed battery — a through-ray, an outside ray, a parallel ray — and confirm each verdict against the known truth. If red tampers, this badge is where it shows.
Two things go in. A ray: an origin O and a direction D, tracing points O + t·D for t>0. A triangle: three vertices v0, v1, v2.
| vertex | x | y | z |
|---|---|---|---|
| v0 | 0 | 0 | 0 |
| v1 | 1 | 0 | 0 |
| v2 | 0 | 1 | 0 |
The triangle is fixed in the z=0 plane. You aim the ray from below it (z=−1) at a target on that plane — the panel solves for the hit. No plane equation is stored; it falls out of the same cross products.
Direction D = (0,0,1): the ray shoots straight up through the z=0 plane. Move the aim to hit inside or miss outside the triangle.
top-down (x,y) — triangle + aim point
Move any control — the verdict is computed from the cross-and-dot products on the spot, never looked up.
What the machine produces, proven. For the canonical through-ray O=(0.25,0.25,−1), D=(0,0,1): distance t = 1, barycentric (u,v,w) = (0.25, 0.25, 0.5) summing to 1, hit point (0.25, 0.25, 0). The reconstructed point O+t·D equals the barycentric point v0+u·e1+v·e2 to 1e−9, and t equals the independent plane-intersection distance.
The blue team's witness (left) confirms these numbers live; the red team (right) tries to make a miss report a hit.
It also finds only the ray/plane crossing — no backface culling, no nearest-of-many. Real tracing needs a BVH to decide which triangles to even ask about; Möller–Trumbore is the question you ask a few of them, not the search.
"Möller–Trumbore is always the fastest." Cut. It wins on minimum storage — no precomputed plane. SIMD packet tracers and precomputed-transform methods beat it on raw throughput; "fast" is relative to 1997's memory budget.
"It handles degenerate triangles." Cut. A zero-area triangle gives det a=0 — indistinguishable from a parallel ray. Degenerates must be filtered upstream, not by this test.
"u,v are the texture coordinates." Kept, corrected. They are barycentric weights on (v1−v0),(v2−v0). Texture uv is an interpolation that uses them — related, not identical.
The red team's move: delete the barycentric range test (accept any u, v) so a ray passing outside the triangle's edges is still reported as a hit. The blue team's witness (window 7) is watching.
Drop the u≥0, v≥0, u+v≤1 guard and the outside ray — which should miss — is falsely reported as a hit. The witness recomputes, disagrees with the known verdict, and turns red. Nothing is faked; the attack is real and it is caught.