Coordinate spaces
Object, world, view, clip and screen space, and why every position and direction must say which one it lives in.
How it works
A vec3 called position is three numbers. Whether (0, 1, 0) means “the top of the model”, “one meter above the
world’s origin” or “just above the camera” depends on the coordinate space it is measured in. Most lighting bugs come
from mixing two values that live in different spaces: the math runs, the compiler is happy, and the image is subtly
wrong. Name the space of every position and direction you use, and only combine values that share one.
The chain
A vertex travels through a fixed sequence of spaces, one matrix at a time:
- Object space (also called local or model space): coordinates as the artist modeled them, relative to the model’s own origin. A sphere’s vertices sit around (0, 0, 0) no matter where the sphere is placed.
- World space: the scene’s shared frame. The model matrix places, rotates and scales the object in it. Lights, the camera and other objects are usually described here, which makes it the common space for lighting.
- View space (eye or camera space): the world as seen from the camera. The view matrix, the inverse of the camera’s own placement, moves everything so the camera sits at the origin. In OpenGL’s convention it looks down -z.
- Clip space: the projection matrix maps the camera’s view volume to a box and puts the depth into a fourth
component,
w. The vertex shader’s job ends here:gl_Positionis in clip space. - Normalized device coordinates (NDC): the GPU divides by
w(the perspective divide), which makes distant things smaller. Visible points end up between -1 and 1 on x and y. On z, OpenGL and WebGL use -1 to 1; Direct3D, Vulkan and Metal use 0 to 1. - Screen space (window space): the viewport transform scales NDC to pixels. This is what
gl_FragCoord.xyholds in the fragment shader, with (0.5, 0.5) at the center of the bottom-left pixel in OpenGL and WebGL.
// vertex shader: object space in, clip space out, world position passed along
uniform mat4 uModel;
uniform mat4 uView;
uniform mat4 uProjection;
in vec3 aPosition; // object space
out vec3 vWorldPos; // world space, interpolated for the fragment shader
void main() {
vec4 world = uModel * vec4(aPosition, 1.0);
vWorldPos = world.xyz;
gl_Position = uProjection * uView * world;
}
The matrices apply right to left: the one next to the vector acts first. The matrices themselves get their own fundamental; here what matters is which space each value ends up in.
Points and directions
A position has a location; a direction only has an orientation. In the fourth component, a point gets Normal A unit vector perpendicular to a surface, pointing the way it faces. Lighting and Fresnel effects compare it with other directions.w = 1.0 and a
direction gets w = 0.0, so a 4×4 matrix moves points but only rotates and scales directions. A light direction
multiplied with w = 1.0 picks up the translation and points somewhere wrong. Normals need one more correction under
non-uniform scale, covered in normalsA unit vector perpendicular to a surface, pointing the way it faces. Lighting and Fresnel effects compare it with other directions.
Picking a space
Lighting works in any one space, as long as every input is in it. Pick the space where most of your inputs already are:
// fragment shader, everything in world space
uniform vec3 uCameraPos; // world space
uniform vec3 uLightDir; // world space, toward the light
in vec3 vWorldPos;
in vec3 vWorldNormal;
// ...
vec3 n = normalize(vWorldNormal);
vec3 v = normalize(uCameraPos - vWorldPos); // from the surface toward the camera
float facing = dot(n, v);
The cards on this site follow that choice: i.position, i.normal and i.view are all in world space, so they can be
combined directly. Engines don’t always agree. In a Godot spatial shader’s fragment(), VERTEX, NORMAL and VIEW
are in view space; multiply by INV_VIEW_MATRIX (with w = 1.0 for the position, 0.0 for directions) to get them
into world space before mixing them with world-space data.
Object space is the right choice for effects that should stick to the model as it moves, like a pattern painted on a rotating object. World space suits effects tied to the scene, such as a scan line sweeping across everything or snow on upward faces. Screen space suits effects tied to the image, like post-processing.