Graphics glossary 27 terms mentioned in this lesson
Select any term for a clear definition. The return button brings you back to the exact term link you used.
Spheres and infinite planes are excellent for exposing ray equations, but most real-time scene geometry is delivered as triangles. A triangle is finite, planar and defined by three vertices. Intersecting its supporting plane is not enough; the resulting point must also lie inside the three edges. A successful test should tell us more than distance because the triangle's three vertex weights will later interpolate normals, texture coordinates and other attributes.
We shall use the Möller-Trumbore form of the ray-triangle test. Its compact code should not be mistaken for an incantation. The operations solve for three unknowns: ray distance and two barycentric coordinates. The third barycentric coordinate follows because the three weights sum to one.
What you should be able to account for
- Construct triangle edge vectors and recognise the parallel determinant case.
- Use barycentric bounds to reject points outside the finite triangle.
- Retain barycentric weights as hit attributes suitable for interpolation.
From vertices to a finite surface
Let the vertices be A, B and C. Two edges from A span the triangle's plane:
E1 = B − A, E2 = C − A
The cross product E1 × E2 gives a face normal whose sign depends on vertex order. Reversing two vertices reverses the normal. This order is called winding. The intersection below can either cull one orientation or accept both. Our manual tracer accepts both and uses the oriented hit-record rule from Lesson 6.
Solve distance and weights together
function hitTriangle(ray, triangle, tMin, tMax) {
const edge1 = subtract(triangle.b, triangle.a);
const edge2 = subtract(triangle.c, triangle.a);
const p = cross(ray.direction, edge2);
const determinant = dot(edge1, p);
if (Math.abs(determinant) < 1e-7) {
return null;
}
const inverse = 1 / determinant;
const fromA = subtract(ray.origin, triangle.a);
const beta = dot(fromA, p) * inverse;
if (beta < 0 || beta > 1) {
return null;
}
const q = cross(fromA, edge1);
const gamma = dot(ray.direction, q) * inverse;
if (gamma < 0 || beta + gamma > 1) {
return null;
}
const t = dot(edge2, q) * inverse;
if (t < tMin || t > tMax) {
return null;
}
const alpha = 1 - beta - gamma;
const outward = normalise(cross(edge1, edge2));
return makeHitRecord(ray, t, pointOnRay(ray, t), outward,
triangle.material, [alpha, beta, gamma]);
}
A near-zero determinant means the ray is parallel to the supporting plane or the triangle is degenerate. The beta and gamma tests constrain the point to the finite triangular region. Non-negative alpha is enforced by beta + gamma <= 1. The usual ray interval then decides whether the otherwise valid triangle point lies along the useful part of the ray.
Barycentric coordinates describe location
A point on the triangle can be written as a weighted combination of its vertices:
P = αA + βB + γC, where α + β + γ = 1
At vertex A the weights are (1, 0, 0). Halfway along edge AB they are (0.5, 0.5, 0). At the centroid they are one third each. Inside the triangle all three are non-negative. This gives both an inside test and a reusable coordinate system over the surface.
Interpolate a vertex colour
Suppose A is red, B is green and C is blue. A hit has weights (0.2, 0.5, 0.3). The interpolated colour is 0.2 × red + 0.5 × green + 0.3 × blue = (0.2, 0.5, 0.3). The same weights can interpolate texture coordinates or vertex normals. What changes is the attribute; the location weights remain the evidence of where the hit lies.
Barycentric laboratory
Move One Point with Three Weights
Change alpha or beta. Gamma is recalculated, the point moves and the triangle states whether that point is inside or outside.
Change this, then watch this: Change α or β. Gamma is derived automatically, P moves immediately and the result changes to OUTSIDE when gamma becomes negative.
- Chosen α at A
- Chosen β at B
- Derived γ at C
- Point P
γ = 1 − α − β; P = αA + βB + γC
What this establishes:
Join the existing scene contract
The laboratory adds a triangle to the sphere-and-plane scene. Its hit record still contains t, position, oriented normal and material, with barycentric coordinates as useful additional data. Closest-hit traversal compares it without knowing it is a triangle. Shadow rays can be blocked by it. The light calculation reads its normal.
Keep the rendered triangle fixed while the diagram interrogates one point using barycentric weights. Otherwise, visible triangle movement becomes a tempting proxy for the weight calculation. Choose α and β; the laboratory derives γ = 1 - α - β. Non-negative weights whose sum is one describe a point inside or on the triangle. A negative derived weight places the reconstructed point outside. Meanwhile, the scene still uses the closest-hit criterion from Lesson 5. A new primitive has added a new intersection equation, not a new definition of visibility.
Degenerate triangles are not small triangles
If A, B and C are collinear or repeated, edge1 × edge2 has zero area and no stable surface normal. Treating the determinant threshold as a complete repair would conceal invalid input. The test can reject the primitive safely, but a mesh-building or loading stage should also identify and report degeneracy.
Very small valid triangles introduce scale and precision questions similar to the shadow bias. A single fixed determinant threshold may reject a triangle after a scene-scale change. Robust intersection code considers relative error and representation. Our threshold is suitable for the present coordinate range and should be challenged, not worshipped.
Classify the weights
Which candidate points are inside or on the triangle: (α,β,γ) = (0.2,0.3,0.5), (-0.1,0.6,0.5), (0,0.4,0.6) and (0.3,0.3,0.3)?
Reveal the classification
The first is inside: all weights are non-negative and sum to one. The second is outside because alpha is negative, even though the sum is one. The third lies on edge BC because alpha is zero and the other weights sum to one. The fourth weights sum to 0.9, so they do not describe a point in the triangle's affine coordinate system until corrected; they are not a valid result of this intersection.
Make the weights visible
Give each vertex one RGB primary colour and display the barycentrically interpolated result. Check the three vertices, three edge midpoints and centroid. Then reverse the winding and record what happens to the outward and oriented normals. Keep back-face culling disabled so the orientation change can be studied separately from visibility policy.
Keep this model for Vulkan
Triangles, vertex buffers, index buffers and interpolated attributes are central to Vulkan graphics. Ray tracing acceleration structures also describe triangle geometry. The later API work will change how vertices are stored and how attributes reach shaders, but barycentric location, winding and the closest-hit requirement remain the same geometric facts.
Our rays can now meet the primitive used by general meshes. Lesson 11 lets a surface create a reflected ray, turning one visibility question into a bounded recursive chain.