Graphics › Coordinate spaces
Screen space
The last hop: stretch the −1…1 NDC square across the window's pixels — and flip y, because on a screen the top row is row zero.
Model space: the cube as the mesh file stores it — built around its own origin, no idea where it'll end up.
the cube's far-top-right corner here: (0.5, 0.5, 0.5)
The viewport transform
NDC runs from −1 to 1 and knows nothing about your monitor. The viewport transformmaps that square onto an actual rectangle of pixels — typically the whole window, but it can be a sub-rectangle (split-screen, a picture-in-picture minimap). For a viewport at (vx, vy) of size w × h:
x_screen = vx + (x_ndc + 1) / 2 · wy_screen = vy + (1 − y_ndc) / 2 · hz_screen = (z_ndc + 1) / 2 (→ the depth buffer, 0…1)
Why y is flipped
Sub-pixel positions and sampling
A vertex usually lands between pixel centres. The rasterizer keeps the fractional position and decides coverage from it — that's the basis of anti-aliasing (MSAA samples several points per pixel; analytic AA computes exact coverage). Rounding too early gives you the jaggies.
Pixels aren't the same as points
On high-DPI screens one CSS “point” is 2 or 3 device pixels, so the framebuffer is bigger than the logical window. The viewport is set in device pixels; UI layout is in points; the devicePixelRatio bridges them. Mixing the two is a classic “why is everything blurry / half-size” bug.