Skip to main content

The Humblest Equation In 3D Graphics - (x', y') = (x/z, y/z)

· 5 min read

This post is about a simple yet foundational equation for rendering 3D objects on a computer screen.

Working in VFX, I have always been curious about the math behind the work we do. I am far removed from any of the heavy lifting on that front, but have wanted to understand, at least at a high level, how this stuff works.

The other day I came across an interesting youtube video. The video outlined how to put together a simple HTML page and some javascript to generate 3D images. The intro explained an elegant and super important equation for mapping the points of 3D objects to a 2D surface. The equation looks like this:

x' = x/z
y' = y/z

x, y, and z represent a point's x, y, and z coordinates in a 3D space, while x' and y' represent where the point lands on the x and y axes of a 2D screen. These relationships date back to Euclid in ancient Greece who understood that as objects get further away, they shrink in size from the perspective of an observer. They've been applied in painting, photography, and now computer graphics.

Applying this equation, we can create a 2D rendering of a 3D cube. A cube has 8 corners (vertices). We can define them in 3D space by combining every combination of -1 and 1 across x, y, and z like so:

const vertices = [
[-1, -1, -1],
[ 1, -1, -1],
[ 1, 1, -1],
[-1, 1, -1],
[-1, -1, 1],
[ 1, -1, 1],
[ 1, 1, 1],
[-1, 1, 1],
];

Now, to project our 3D object onto a 2D screen, we need to use our equation:

function project(x, y, z) {
return {
x: x / z,
y: y / z
};
}

I'll add an offset the shape so that z doesn't equal 0 for now, and draw the edges so we can see the cube's faces, and I'll make an html canvas so we can see what we're dealing with. Here's our cube:

Representation of a 3D object on a 2D screen.

Now that we have our 3D image, it's worth trying to make it rotate. Such a core function of modern 3D software is the ability to look at an object from different angles.

To do this, I can create a new function and call it rotate, and have it accept the x,y,z coordinates and the angle I want to rotate by:

function rotate_xz({x,y,z}, angle){
}

What we actually put in this function has to do with this equation here:

x' = x·cos(θ) - y·sin(θ)
y' = x·sin(θ) + y·cos(θ)

This is a 2D rotation matrix, it rotates a point around the origin by angle θ. Think of a point on a unit circle: rotating it by θ means its new x is how far it is along the horizontal, and its new y is how far it is along the vertical. In our case let's try rotating our cube left to right on the y-axis.

3D rotation is just that same formula applied to a chosen trio of axes, but leaving the third untouched, because we're rotating in a single plane at a time, the axis perpendicular to that plane acts as the pivot, it doesn't participate in the rotation at all... That's what rotate_xz is, and in our case what we want, rotation applied to the X and Z axes while Y stays fixed:

x' = x·cos(θ) - z·sin(θ)
y' = y ← untouched
z' = x·sin(θ) + z·cos(θ)

The three standard rotations (around X, Y, or Z axis) are essentially different choices of which axis to leave alone:

AxisUntouchedRotating pair
Xxy, z
Yyx, z
Zzx, y

All 3D rotation ultimately comes back to that 2D formula, but there are a ton of other ways of expressing these same underlying relationships.

θ in this relationship will be a time-dependent angle we increment each frame. We'll calculate the points as they rotate, then project them onto our 2D screen.

With some adjustments to our function:

function rotate_xz({x,y,z}, angle){
const cos = Math.cos(angle);
const sin = Math.sin(angle);
return{
x: x*cos-z*sin,
y, //<--stays y
z: x*sin+z*cos,
}
}

Here's our rotating cube:

Representation of a 3D object rotating on a 2D screen.

Now that we have our raw 3D points to 2D rendering pipeline setup, we can grab the raw 3D point data from any .obj file and project it on our 2D HTML canvas. Here's an homage to Ed Catmull's 1972 A Computer Animated Hand.

Representation of a 3D hand rotating on a 2D screen.