Vectors and matrices

00-vectors-and-matrices.md · updated 2026-10-15

For the pure-maths intuition — cos/sin, what a dot is, cross, SDFs — see ../math/trig-and-vector-heuristics.md. This note is the same few operations seen from inside a renderer or a physics step: which one answers which question, and how each goes wrong.

A vector is a point or a direction, never both

Points move when the world moves; directions don't.

The same three numbers mean "here" or "that way", and transforms carry translation — but a normal pointing up still points up after the object slides sideways. A fourth component tags which you meant: w=1 says point, transform me fully; w=0 says direction, skip the translation column.

Normalize: keep the direction, forget the distance

Normalize when the length would corrupt the answer, not as a reflex.

Unit vectors make dot products come out as pure cosines — normals, light and view directions and camera axes all must be unit. It costs a square root, so skip it where length cancels, and never take a root just to compare distances: distSq < r*r is exact, and sorting by squared distance gives the same order.

Dot: how much do these agree

One number for "same direction" — most lighting and visibility tests are built from it.

Cross: the perpendicular you asked for

Square to both inputs, with length equal to the parallelogram they span.

A triangle's normal is normalize(cross(b-a, c-a)); winding picks which side is "out". A camera basis is the same trick: forward, then right = normalize(cross(worldUp, forward)), then up = cross(forward, right) — three arrows that are a rotation matrix. In 2D it collapses to a scalar, a.x*b.y - a.y*b.x, whose sign says whether B is left or right of A and whose value is the signed area that decides winding, hence backface direction.

A matrix is where the axes land

Read the columns as arrows: where x̂ lands, where ŷ lands, where ẑ lands, and where the origin lands.

The last column is translation because it is the origin's new home — and that is what a 4×4 buys over a 3×3: rotation, scale and translation in one multiply. Order matters: T*R*S scales, rotates, then moves (read right to left); S*R*T scales the translation too.

Normals transform by the inverse transpose. Non-uniform scale shears angles — squash a sphere and its surface tilts one way while a naively scaled normal tilts the other, so lighting breaks on exactly the stretched objects.

Orientation

Euler angles are three sequential rotations — readable, order-dependent, and at some poses two axes align and a degree of freedom vanishes (gimbal lock: the camera that can't pitch past vertical). Quaternions are the thing you use instead: four numbers that rotate without that failure, converted to a matrix on the way to the GPU. slerp blends two at constant angular speed — rotations interpolated without the wobble.

Demo

A vector playground — drag A and B: the agreement bar in Dot, the parallelogram in Cross, the determinant collapsing to a line in Basis, the same dot as shading in Lighting.

Gotchas

Symptom Cause
Lighting shifts when an object only translatesA direction was transformed as a point — it picked up the translation column
Normals wrong only on squashed or stretched meshesNon-uniform scale; transform normals by the inverse transpose
Surface too dark or blown out after a transformNormal or light direction no longer unit length — renormalize
Object orbits the origin instead of spinning in placeRotation applied after translation; rotate first, then translate
Backfaces culled on the wrong sideWinding flipped — a mirror or negative scale inverts the cross product
Camera jams or flips when looking straight upGimbal lock, or cross(worldUp, forward) degenerating as forward ≈ worldUp
Rotation tween swings the long way round or stuttersLerping Euler angles or raw quaternion components; slerp, and normalize after
Distance checks show up in the profileA sqrt inside a comparison — compare squared distances
Transform looks right alone, wrong when parentedMultiplication doesn't commute; check the order the matrices are concatenated

Open the demo →

← all notes & demos