Gabriel O’Flaherty-Chan spent his early years on frameworks that handed him a Camera that “just kind of worked” — and admits he lacked the vocabulary to even search for the camera behaviour he wanted until he wrote lower-level graphics code. So he wrote the missing explainer, with interactive shader examples you can drag.
The whole thing starts from one line:
x' = x / z,y' = y / z— withyup andzforward, that is a 3D point projected into 2D- Points that differ only in depth march toward the vanishing point at
(0,0):(2,1,2) → (1,0.5),(2,1,4) → (0.5,0.25),(2,1,8) → (0.25,0.125) - The same division handles size:
radius = 0.5 / zis why the demo ball shrinks as it orbits away - It scales to “geometry” — a rotating cube is 12 line segments, each with both endpoints projected the same way
Then the honest part. The naive trick only works for camera-relative points in simple scenes. Once you need camera position, direction and field of view, the practical answer is the perspective projection matrix — parameterized by FOV, aspect ratio, and near/far clipping planes, which double as the test for what is in frame and what can be culled:
f = 1 / tan(θ/2), witha = width / height- Depth mapping
A = (F+n)/(F−n),B = −2Fn/(F−n) - After the matrix multiply and the divide by
w, you getx_ndc = (f/a)·x/zandy_ndc = f·y/z - Set
f = 1anda = 1and you are back atx/z— the trick was the matrix all along, just with the terms stripped out
Naming the stages is most of the value: world space → view space → clip space → divide by w → NDC → pixels → rasterization. NDC coordinates no longer mean distances; they mean where the point lands inside the camera’s visible bounds.
The takeaway is the one the author wanted when he started: a camera is a handful of transformations, and once you can name them you can implement only the parts you need. Sometimes that is the full pipeline. Sometimes it is one division.