Back to lab

Why game development?

Game development is one of the central, if not the most central, pipeline for people to get into programming. I still remember being 1212 years old and the intrigue I felt when my father tried to teach me basic Java so that I could make mods in Minecraft, just for me to give it up almost immediately after writing my first Hello World. Several years later I spent IT classes writing games in Javascript thinking I wanted to get into game development. While I do not aspire to that anymore, game development is still very exciting, and the techniques behind it even more so.

When I was getting into it I was watching tons of YouTube videos behind it and gradually became more interested in the process than the result. For maximal indoctrination I highly, highly, highly recommend the Sebastian Lague \rightarrow Freya Holmér \rightarrow 3Blue1Brown pipeline!

How to render a cube

The first demo is deliberately small: a cube rendered on the CPU.

This is not how one would normally draw a cube in a serious game engine. If the goal were merely to display a cube, I would use WebGL or Three.js and let the GPU do its job. Here the goal is different. I want the steps to be visible.

A cube begins as eight points in its own local coordinate system:

(±1,±1,±1).(\pm 1,\pm 1,\pm 1).

The renderer rotates those points, moves them in front of a camera, projects them onto the screen, and then draws the resulting faces and edges.

Renderer

Projection

Interaction

Drag the cube to rotate it. The scene is small because large scenes get really slow when rendered in the manner used here. I have a really old project on GitHub which illustrates this.

That tiny pipeline already contains the main idea of 3D graphics:

3D geometry2D screen coordinates.\text{3D geometry} \longrightarrow \text{2D screen coordinates}.

To rotate the cube, we apply rotations to its vertices. For example, rotation around the yy-axis is given by

(cosθ0sinθ010sinθ0cosθ).\begin{pmatrix} \cos\theta & 0 & \sin\theta \\ 0 & 1 & 0 \\ -\sin\theta & 0 & \cos\theta \end{pmatrix}.

Now, the matrix-multiplication is perhaps not that useful for our intuition. In reality, the matrix above just represents the aggregation of multiple transformations which when completed look like a rotation around the yy-axis, the matrix is simply a more compact way of describing this series of transformations.

Thus we get our first glimpse at the machinery behind most of 3D-graphis: linear algebra. That’s right, the math which plagues many stem-majors’ undergraduate education informs the techniques used to draw graphics in video games for our enjoyment.

In a full graphics pipeline, the same idea is organized into several coordinate systems:

model spaceworld spacecamera spaceclip spacescreen space.\text{model space} \to \text{world space} \to \text{camera space} \to \text{clip space} \to \text{screen space}.

First describe the object in its own coordinates. Then place it in the world. Then describe where it is relative to the camera. Then decide what the camera can see. Finally, turn the visible geometry into pixels.

Perspective projection

With the above steps, we get the orthographic view which you can check out in the demo. As you may see, the image is lacking in depth, so that is our next step.

A point in space has coordinates

(x,y,z),(x,y,z),

but the screen only has two dimensions. Perspective projection turns that point into something like

(xscreen,yscreen).(x_{\text{screen}}, y_{\text{screen}}).

The rough idea is

xscreenxz,yscreenyz.x_{\text{screen}} \sim \frac{x}{z}, \qquad y_{\text{screen}} \sim \frac{y}{z}.

The larger the depth zz is, the smaller the projected coordinates become. This is why distant objects appear smaller.

Orthographic projection removes this division by depth. Parallel lines stay parallel, and distant objects do not shrink. That is useful for technical views, strategy games, editors, and diagrams, but it does not mimic ordinary visual perspective.

Why triangles appear everywhere

The cube has square faces, but the demo can show each square split into two triangles. Real-time graphics is largely built around triangle meshes.

Triangles are convenient because three points always determine a flat surface. A complicated model can therefore be approximated by many small triangles. A cube is only

6 faces×2 triangles per face=12 triangles,6 \text{ faces} \times 2 \text{ triangles per face} = 12 \text{ triangles},

but the same idea scales to characters, terrain, buildings, and almost everything else you see in a 3D game.

A triangulated mesh increasing in detail
Triangle count increasing on a simple mesh.

A real renderer would clip those triangles against the camera frustum, rasterize them into fragments, shade them, depth-test them, and finally write pixels to the screen. This demo is not so sofisticated as it simply sorts the cube faces by depth and draws them back-to-front using Canvas 2D. That is called the painter’s algorithm. It works fine here, just know that it is not robust enough for arbitrary 3D scenes.

Why this is a CPU renderer

This is a tiny CPU renderer because the point is to expose the machinery.

Three.js would be better engineering if all we wanted was a nice cube. But for a demo, hiding the matrix math inside a library would defeat the purpose. This little renderer has no z-buffer, no clipping, no lighting model, no texture mapping, and no perspective-correct interpolation. It only has the skeleton:

verticesrotationsprojectionscreen-space triangles.\text{vertices} \to \text{rotations} \to \text{projection} \to \text{screen-space triangles}.

Hopefully this is sufficiently elcudating such that some readers will look deeper into the even more fascinating techniques used to make even more interesting things appear on the screen!

I highly recommend checking out Acerola for anyone interested in 3D graphics more broadly!

Further rabbit holes

This demo connects to one of my earliest graphics rabbit holes. My first real foray into rendering from scratch was following Software Rendering from Scratch.

Some other resources I like for this topic:

Notes for myself on things to add