LerpworksAcademy
On this page

Lesson by steps

Spaces: where a point is

17 steps

  • Why this matters
  • One point, many addresses
  • The path of a vertex: a chain of spaces
  • The matrix: axes and an origin
  • Camera and perspective
  • Points, directions and normals
  • Spaces in a material
  • Far from the origin of the world
  • Where it breaks
  • Connections: the same idea in other fields
  • Key points

Graphics › Space

Spaces: where a point is

Know first: Shader: a function for every pixel

This lesson is part of the course “How an engine draws a frame”.

A single point on a rock has many addresses. Measure it from the model’s center, the world’s origin, the camera or a corner of the screen, and you get four sets of numbers. All four are correct. This first lesson of the course “How an engine draws a frame” follows a vertex through that chain of spaces, from the model file to a pixel. It shows what the matrices along the way do, and why a material so often has to convert an arrow from one space to another.

Why this matters

The lesson on shaders said it in one line: the vertex shader turns a position in model coordinates into a position on the screen. This lesson shows how.

An artist needs this as much as a programmer does. Moss grows on the side of a rock after you rotate it. A texture slides across a moving boat. Wind sways neighboring trees in different directions. A model from Blender arrives in the engine lying on its side and a hundred times too small. In every one of these cases the arithmetic is correct, but it was done in the wrong space. Half the nodes in an Unreal material deal with coordinates: World Position, Object Position, Camera Vector, Transform. Each works in its own space.

One point, many addresses

You already know this from daily life. Where are your keys right now? “In my jacket pocket” if you measure from yourself. “Two meters from the door” if you measure from the room. “On the third floor of the building on the corner” if you measure from the city. The place is the same, but each address counts from a different starting point. The addresses also nest. Put the keys in a bag and the bag in a car. When the car drives off, the bag and the keys go with it, even though inside the bag the keys have not moved.

3D works the same way, with more spaces, each with its own name:

  • Object space (also called local space): coordinates measured from the model’s pivot, along the model’s own axes. This is how points are stored in the model file. The nose of a boat is “1.5 meters forward of the pivot,” wherever the boat stands.
  • World space: coordinates measured from the origin of the level. Everything shares it. Objects are placed in it, and distances are measured in it.
  • View space (or camera space): coordinates measured from the camera, so far forward, so far right, so far up.
  • Screen space: where the point lands on the screen, a pixel across and a pixel down, plus depth.
nose of the boat in object space:  (1.5, 0)          // from the model file, in meters
boat: pivot at (20, 10) in the world, rotated 90°
in the world:     (20, 10) + 1.5 × (0, 1) = (20, 11.5)  // the boat’s X axis now points along the world’s Y
camera at (20, 0), looking along +Y
from the camera:  11.5 m forward, 0 m right
on the screen:    exactly in the middle

The path of a vertex: a chain of spaces

The vertex shader walks every vertex down a chain. At each step it switches to a new frame of reference:

object ──(the object’s transform)──→ world ──(the camera’s transform, reversed)──→ camera
       ──(projection)──→ clip ──(divide by depth)──→ NDC ──→ screen pixels
  • Object → world. Scale the vertex, rotate it, and move it to where the object stands. These three values (three triples, to be exact) are the transform in the Details panel.
  • World → camera. The camera does not actually move. The graphics card shifts and rotates the whole world so that the camera ends up at the origin, looking along its own axis. It does this by applying the camera’s transform in reverse.
  • Camera → screen. Projection and a division by depth make distant things smaller. The perspective section explains how.

Try it

A view from above: a boat, a camera and the origin of the world. Move and rotate the boat, and rotate the camera. Choose which space the picture is drawn in. In object space the boat stays still while the world turns around it. In camera space the camera stands at the bottom, looking up, and everything else turns. The numbers below are the addresses of the boat’s nose in each space.


Notice that in object space the nose of the boat always sits at the same point, however you move or turn the boat. So a model is stored in its own coordinates, and you can put it anywhere without touching the file. The nose’s world coordinates change with every move of the boat. Its camera coordinates change with those moves and with every turn of the camera.

The matrix: axes and an origin

How do you convert a point from object space to world space? Suppose you know where the boat’s pivot is and which way its axes point in the world. Take the point “1.5 forward and 0.3 sideways.” Start at the pivot, go 1.5 steps along the boat’s X axis, then 0.3 steps along its Y axis:

world = pivot + x × (object’s X axis) + y × (object’s Y axis) + z × (object’s Z axis)

All you need is an origin and three axes, written in world coordinates. The transform’s matrix is exactly that, arranged in a table: the columns are the object’s axes, and the last column is the pivot. Rotation turns the axes, scale changes their length, and translation moves the pivot.

  • Order matters. Engines apply scale first, then rotation, then translation. Move the object first and rotate it afterward, and it swings around the origin of the world and flies off along an arc. Walking works the same way: “a step forward, then a turn to the right” and “a turn to the right, then a step forward” end in different places.
  • The way back is the inverse matrix. To go from the world back into object space, you use a matrix that undoes the transform: move back, rotate back, divide by the scale. World → camera is the inverse of the camera’s transform matrix.
  • A hierarchy is a product of matrices. Take a lantern in a character’s hand. Its transform is given relative to the hand, the hand’s relative to the arm, and the arm’s relative to the character. The lantern’s world matrix is the product of the whole chain, like the keys in the bag in the car.
Homogeneous coordinates: why a fourth number

Rotation and scale are a multiplication by a 3 × 3 matrix, but translation is an addition. To make the whole chain a single multiplication, you add a fourth coordinate, w = 1, to the point. The matrix becomes 4 × 4, with the translation in its fourth column. These are called homogeneous coordinates.

| X.x  Y.x  Z.x  P.x |   | x |   | P.x + x·X.x + y·Y.x + z·Z.x |
| X.y  Y.y  Z.y  P.y | × | y | = | P.y + …                     |
| X.z  Y.z  Z.z  P.z |   | z |   | P.z + …                     |
|  0    0    0    1  |   | 1 |   | 1                           |

The w trick solves another problem. A direction, such as a normal, the wind or a velocity, must not be moved: an arrow has no position, only a heading. Directions get w = 0, so the translation column has no effect on them. So shaders have separate functions for points and for vectors, and Unreal has the nodes TransformPosition (for points) and Transform (for directions).

The whole chain “object → world → camera → projection” is three 4 × 4 matrices. The CPU multiplies the camera and projection matrices together in advance, once per frame. The object → world step stays a separate multiplication, because the material, the wind and the light all need the vertex’s world position. So the vertex shader does two multiplications per vertex. Older engines merged all three matrices into one.

Camera and perspective

In camera space, the camera sits at the origin and looks along its own axis. What remains is to turn the three coordinates “forward, right, up” into a point on a flat screen.

Hold a ruler up in front of one eye. A post 10 meters away covers a certain number of centimeters on the ruler. An identical post 20 meters away covers half as many. Size on the screen is inversely proportional to distance. That is perspective, and in a formula it is a division by depth:

screen.x = f × right / forward               // −1 and 1 are the edges of the frame
screen.y = f × aspect × up / forward         // aspect = width / height, 1.78 for 16:9
f = 1 / tan(FOV / 2)                         // FOV = field of view, horizontal: 90° → f = 1,  60° → f ≈ 1.73

The aspect factor is needed because the frame is not square. It is shorter than it is wide, so the same scale from −1 to 1 has to fit a smaller angle. Without the factor, the picture would be stretched vertically. If the field of view is given vertically, as in Unity, it works the other way around: f is computed from the vertical angle, and aspect divides screen.x.

For example, take a horizontal FOV of 90° and a screen 1920 pixels wide. An object 1 meter wide at a distance of 10 meters takes up 1 / 10 = 0.1 of half the frame, which is 96 pixels. At 20 meters it takes 48. With a 60° FOV, the same object at 10 meters takes about 166 pixels: a narrow field of view works like a zoom.

Try it

A side view: the camera on the left and three posts, each 2 meters tall. Move the nearest post and change the field of view. Here the angle is vertical, so f is computed from the height of the frame. On the right is how the posts look on a screen 1080 pixels tall.


Division by depth is the only nonlinear operation in the whole chain, so it gets a step of its own. The projection matrix does not divide. It puts the depth into the fourth coordinate, w. After the vertex shader, the graphics card divides x, y and z by w itself. This is the perspective divide. The result is normalized device coordinates (NDC): everything visible lies between −1 and 1 in x and y. Whatever falls outside is beyond the edge of the frame and gets clipped.

Clip space, near and far

After the projection matrix and before the division, the coordinates are in clip space. This is where the graphics card cuts off triangles that stick out of the frame. The test “x between −w and w” is simpler than a test after the division, and it keeps working for points behind the camera, where the depth is negative.

The camera has a near plane and a far plane, called near and far. Anything closer than near or farther than far is not drawn. The depth between them is converted to a number from 0 to 1 and written to the depth buffer. The graphics card uses that buffer to decide what hides what. The lesson on the depth buffer explains how that number is built and why its precision is spread unevenly.

The last step is the viewport: from −1…1 to pixels. For a 1920 × 1080 screen, that is pixel = (x + 1) / 2 × 1920. The y axis gets flipped on the way: in NDC, up is positive, but pixels are counted from the top down.

Points, directions and normals

Three different kinds of things can be converted from one space to another, and each follows its own rules.

  • A point is a position. All three steps apply to it: scale, rotation and translation.
  • A direction is the wind, a velocity, the direction to the sun. It can be rotated and scaled but never translated: an arrow has no place, only the way it points.
  • A normal is an arrow that stands perpendicular to a surface. Under an ordinary rotation and a uniform scale, it behaves like a direction. But stretch an object along one axis, and a normal turned by the same matrix is no longer perpendicular.

Try it

A circle is stretched horizontally. The red arrows are normals converted as ordinary directions. The green ones are converted correctly. Stretch the circle further and compare each set with a true perpendicular.


The right matrix for normals is the inverse transpose of the transform matrix. For a rotation and a uniform scale it matches the ordinary matrix (up to length), so you see a difference only under non-uniform scale. Engines handle this for mesh normals. But if you compute an arrow of your own in a material and convert it by hand, keep it in mind.

Spaces in a material

One rule covers it: you can add two arrows, compare them or take their dot product only if both are in the same space. Otherwise the arithmetic works but the result means nothing, like adding meters to feet.

TaskSpaceWhy
Moss, snow and dust on topworld“Up” is the sky, not the model’s axis. Otherwise the moss slides onto the side once the rock is rotated.
Wind for grass and treesworldWind blows in the world. Neighboring trees must sway the same way, however they were rotated when placed.
A texture shared across joined modules or along a slopeworldThe pattern does not break at the borders between pieces. More in the lesson on triplanar mapping.
An effect that travels with an objectobjectA pattern on a boat or a barrel must not slide when it moves.
A normal maptangentThe arrows are recorded relative to the surface, which is why the texture works on any model. Tangent space
A vignette, screen effectsscreenThese need the pixel’s position in the frame, not in the world.

Engines and 3D programs follow different conventions for axes and units, so models arrive lying down or tiny:

ProgramUpHandednessUnit
UnrealZleftcentimeter
UnityYleftmeter
GodotYrightmeter
BlenderZrightmeter

Left- and right-handed systems differ in the direction of one axis. Hold out your right hand with the thumb along X and the index finger along Y. The middle finger points along Z in a right-handed system. In a left-handed system, Z points the opposite way. When you move a model between them, one axis has to be flipped, or the model comes out mirrored. Exporters do this for you if the axes are set correctly. The flow on exporting from Blender to Unreal shows how.

Far from the origin of the world

Coordinates have one more trap: precision. An ordinary floating-point number (a float) holds about seven significant digits. Near the origin of the world that gives fractions of a millimeter, but farther out the step grows. At 10 km from the origin, a float can tell positions apart only to 0.6 mm. At 100 km it is whole centimeters. Vertices start to jump along that grid: models shake, shadows flicker, animation judders.

So large worlds either keep the player near the origin and shift the world around them (origin rebasing), or calculate positions in double precision. Unreal 5 does the second: its Large World Coordinates are stored as doubles. In a material, the Camera Relative variants of World Position are both more precise and cheaper than the absolute ones, because the numbers near the camera are small.

Where it breaks

  • Moss grows on the side of a rock after you rotate it. “Up” is taken in object space. Compare the normal with the world’s up, (0, 0, 1), and convert the normal to world space first.
  • A texture slides across a moving object. The texture coordinates come from the world. For anything that moves, use object coordinates or UVs.
  • Neighboring trees sway in different directions. The wind is defined in object space, so it turns with each tree. Define the wind direction in the world.
  • A model arrives lying down, mirrored or a hundred times too small. The programs use different axes and units. Check the axes and the scale in the export settings instead of rotating each model by hand.
  • The lighting from a normal map looks strange after your own math. The arrows from the normal map, which are in tangent space, were added to arrows in world space. Convert them to the same space first.
  • An object stretched along one axis is lit crookedly in your effect. The normal was converted as an ordinary direction. You need the inverse transpose matrix, or a node that applies it for you.
  • Models shake far from the origin of the level. A float does not have enough precision. Keep the play area closer to the origin of the world, or calculate relative to the camera.

Key points

  • A point has many addresses: in object, world, camera and screen space. All are correct. They are measured from different origins.
  • A vertex walks down a chain: object → world → camera → clip → divide by depth → pixels.
  • The transform matrix is the object’s axes and pivot, written in world coordinates. The order is scale, rotation, translation.
  • World → camera is the camera’s transform in reverse. The camera does not move; the world does.
  • Perspective is a division by depth: twice as far means half the size. A narrow field of view works like a zoom.
  • Points are translated and directions are not. Normals under a non-uniform scale need the inverse transpose matrix.
  • Only arrows from the same space can be added or compared. Moss and wind live in the world, a pattern on a moving object in object space, and normals from a normal map in tangent space.

Test yourself

8 questions. Your best result goes to your profile; take it as many times as you like.

1. A boat is moved 10 meters. Which coordinates of its nose did not change?

2. After a rock is rotated 90°, the moss has moved from the top to the side. What is wrong in the material?

3. How does the graphics card “move the camera” through the level?

4. A post 10 meters away is 190 pixels tall on the screen. How tall will it be at 20 meters, with the same field of view?

5. Why can’t the direction of the wind be converted to another space with the same formula as a point?

6. An object is stretched to twice its width along X. The normals were converted with the same matrix as the points. What happens?

7. A model from Blender arrived in Unreal a hundred times smaller. Why?

8. At 100 km from the origin of the world, models began to shake. What is the cause?

Something still unclear? Select that piece of text and press “Ask about this”. The question goes to the author, and the place gets rewritten more clearly.