Vectors & Dot/Cross Products in Geometric Context
Geometry & Trigonometry
Chapter 4 · Vectors & Dot/Cross Products in Geometric Context
Linear Algebra Fundamentals defined the dot and cross products algebraically. This chapter puts them to work on genuinely geometric problems: decomposing a vector into useful directional components, finding the direction a surface faces, and computing how brightly a light illuminates it — three of the most common building blocks in real graphics and game code.
Vector Projection: Splitting a Vector Into Two Useful Parts
Given a vector a and a direction b, the projection of a onto b is the component of a that points along b: proj_b(a) = (a·b / b·b) · b. Whatever's left over, a − proj_b(a), is the component of a perpendicular to b.
a=(5,3) projected onto b=(2,1): proj_b(a) = (5.2, 2.6), and the perpendicular remainder is (−0.2, 0.4). Two independent checks confirm this is correct: proj + perp recovers (5.0, 3.0) — exactly the original vector a — and the dot product of the perpendicular component with b is ≈−4.44×10⁻¹⁶, effectively zero, confirming the two really are perpendicular.
This decomposition is exactly how a game physics engine splits gravity into "the component pulling a character down a slope" (the projection onto the slope's own direction) and "the component pressing into the slope" (the perpendicular remainder) — two physically meaningful quantities recovered from one vector and one direction.
Surface Normals via the Cross Product
Every triangle in a 3D mesh has a direction it "faces" — its normal vector, perpendicular to the triangle's own surface. Given two of the triangle's edges as vectors, the cross product gives exactly that: normal = edge1 × edge2, normalized to unit length.
A=(0,0,0), B=(2,0,0), C=(0,3,1): the raw cross product of edge1=B−A and edge2=C−A gives (0, −2, 6), normalizing to the unit vector (0, −0.3162, 0.9487), confirmed to have length 0.9999999999999999≈1. Two independent checks confirm it's genuinely perpendicular to the triangle: the dot product of the (unnormalized) normal with both edges comes out to exactly 0.
edge1 × edge2 and edge2 × edge1 point in exactly opposite directions (the cross product anticommutes). Which order a mesh format uses determines whether a triangle's normal faces "outward" or "inward" — get it backward, and every triangle in a model appears to face the wrong way, a real and common source of "inside-out" looking 3D models.
Lighting: Why the Dot Product Needs a Clamp
The simplest realistic lighting model, Lambertian (diffuse) lighting, computes a surface's brightness as max(0, normal · light_direction) — the dot product between the surface normal and the direction toward the light.
normal · light_direction ≈ 0.6069 — a sensible, positive brightness. With the exact same light instead positioned behind the surface (the mirror-image direction), the dot product comes out to ≈−0.6069 — a negative brightness, which is physically meaningless (a surface can't emit negative light). The max(0, ...) clamp exists exactly to catch this: it correctly reduces the negative result to 0, meaning "this surface receives no light from this direction at all."
max(0, ...) and using the raw dot product directly is a genuinely common graphics bug — a negative brightness value fed straight into a color channel either gets silently clamped somewhere else in the rendering pipeline (masking the bug) or produces visibly wrong, "negative-lit" dark patches on surfaces facing away from every light source. This is exactly the same defensive-clamping instinct as checking a value's valid range before using it — familiar territory from Numerical Methods & Floating-Point Computation's own emphasis on not trusting a raw computed value without checking it makes sense.
Bonus: Which Side of a Line Is a Point On?
The 2D cross product's sign (not magnitude) answers a genuinely useful question directly: for a line from P1 to P2, and a point Q, the sign of cross(P2−P1, Q−P1) tells you which side of the line Q is on.
(0,0) to (4,0) (the x-axis): a point (2,3) above the line gives a cross-product value of +12; a point (2,−3) below the line gives −12 — same magnitude, opposite sign, exactly tracking which side each point falls on.
Chapter 9 builds this exact sign test into a full point-in-polygon and line-intersection toolkit.
Where This Connects
| This chapter's finding | What it sets up |
|---|---|
| Vector projection splits a vector into parallel/perpendicular parts | The literal mechanism behind resolving a rotation into axis components in Chapter 5-6 |
| Cross product order determines normal direction | The same ordering sensitivity reappears in Chapter 5's rotation composition, where order changes the result |
| The 2D cross-product sign test for "which side" | Reused directly as the core mechanism behind Chapter 9's line-intersection and point-in-polygon tests |
Hands-On Exercises
Using this chapter's own projection formula, decompose a=(6,2) onto the direction b=(1,3). Compute the parallel and perpendicular components, and verify both that they sum back to a and that the perpendicular component is orthogonal to b.
A 3D model appears "inside-out" after being loaded — every surface that should be visible from outside is instead invisible (culled), and vice versa. Using this chapter's own explanation of cross-product order and normals, explain the most likely cause and how you would confirm it.
📄 View solutionUsing this chapter's own verified lighting example, explain why a renderer that skips the max(0, ...) clamp might still "look correct" in many scenes during testing, and describe a specific scene setup where the bug would become clearly visible.
Chapter 4 Quick Reference
- Vector projection:
proj_b(a) = (a·b/b·b)·b; the perpendicular remainder isa − proj_b(a)— verified self-checking: the two parts sum back toa, and are mutually orthogonal (dot product≈0) - Surface normal:
edge1 × edge2, normalized — verified genuinely perpendicular to both triangle edges (dot products exactly0) and unit length - Cross-product order matters:
edge1×edge2 ≠ edge2×edge1— reversing it flips which way a normal (and therefore a whole model's visible surfaces) faces - Lambertian lighting:
max(0, normal·light_direction)— verified a light positioned behind a surface gives a negative dot product (≈−0.6069), which the clamp correctly reduces to0 - The 2D cross product's sign tells you which side of a line a point is on — verified
+12above vs.−12below the same line - Next chapter: 2D rotations and rotation matrices — a deeper pass on Linear Algebra Fundamentals' own brief rotation-matrix introduction