3D SOFTWARE, 1992
In 1992 there was no GPU, so 3D was arithmetic. This era builds a renderer by hand: a tile map, a camera, DDA raycasting, textured walls, fixed-point maths, BSP sectors and depth sorting, 320 columns at a time.
Graphics track · 29 missions · boss mission, written exam and certificate · free, no signup. Everything below runs in the browser terminal on the SERVBG home page.
What you will do
- see the reference scene that becomes this era's wall texture
refWolfenstein 3D shipped in May 1992 at 320x200 with 256 colours, and every wall in it was a 64x64 picture pasted onto a flat surface.
- read the 16x16 tile map the whole world is stored in
mapA Wolfenstein 3D level was a 64x64 grid of bytes. One byte per cell said solid or empty and which texture to use, so a whole floor fit in 4 KB.
- inspect the camera: a point, an angle, a field of view
camThe 1992 view model was two coordinates and an angle. No pitch, no roll, no eye height — those were not omissions to fix later, they were what made the maths cheap enough to run.
- cast one ray and watch the DDA step grid line to grid line
rayThe digital differential analyser walks a ray across cell boundaries instead of sampling it in small steps, so it never overshoots a thin wall and never wastes a step inside empty space.
- draw all 320 columns for the first time
renderOne ray per screen column, one vertical strip of wall per ray. John Carmack picked that constraint because a vertical strip is a single tight inner loop with no perspective correction anywhere in it.
- widen the field of view and see more of the corridor
fov 90Field of view is only how much angle the 320 columns are spread across. Widen it and each column covers more world, which is why the edges stretch.
- measure the barrel distortion column by column
fisheyeUsing the raw ray length makes a flat wall look curved: the edge rays travel further to reach the same surface, so their slices come out shorter.
- multiply by the cosine of the angle difference
fisheye fixMultiplying the ray length by cos(ray angle minus camera angle) projects it onto the camera forward axis. That single cosine is the difference between a bowed scene and a straight one.
- settle on a 60 degree field of view
fov 60Most 1990s shooters sat between 60 and 90 degrees horizontally. Below that the world feels distant; far above it the edge stretch becomes hard to ignore even with the cosine fix.
- read every number the inner loop computes for one strip
column 160The texture v step is the whole texture mapper: divide 64 by the slice height once, then add that value per scanline. No division inside the loop.
- list the three 64x64 wall textures
texturesTextures were 64x64 because 64 is a power of two: the texel address is a shift and a mask instead of a multiply and a divide. 4096 bytes each at 8 bits per pixel.
- sample a texture per column instead of filling flat colour
texture onFlat-shaded walls were the 1991 look. Texture mapping every column is what made Wolfenstein 3D feel like a place rather than a diagram.
- darken walls with distance
shade onDoom did not multiply anything per pixel to do this. It held 32 pre-darkened copies of the palette and chose one by distance, so shading cost a table lookup.
- step forward and watch the slices grow
forwardNothing is cached between frames. Every move rebuilds all 320 columns from scratch, which is why frame rate scaled with screen width and not with level size.
- turn 15 degrees clockwise
turn rightTurning changes one base angle. The 320 per-column offsets stay the same, and in 1992 they lived in a precomputed table so a turn cost no trigonometry at all.
- turn back to face down the corridor
turn leftCollision was a tile lookup: convert the new position to integers, read the map byte, refuse the move if it is not zero. No geometry, no physics.
- back up to the west end of the corridor
step backWolfenstein 3D strafed with Alt held down while you pressed left or right. Doom in 1993 gave strafing its own keys and added a run key, and its mouse turned the player without ever aiming up or down.
- read the sin table and the 1/z table in 16.16 fixed point
tablesA 386 divide cost around 40 cycles and a table lookup around 4. Precomputing sines and reciprocals into a kilobyte of memory was the defining optimisation of the era.
- convert one number into 16.16 fixed point and back
fixed 3.1415916.16 fixed point stores 1.0 as 65536 in an ordinary 32-bit integer. Add and subtract are plain integer ops, multiply is a multiply then a shift right by 16.
- learn the level format that replaced the tile grid
sectorsDoom, December 1993, replaced the grid with sectors: closed areas with a floor height, a ceiling height and a light level. It bought angled walls and stairs, and it still could not put one room above another.
- split six walls into a binary space partition tree
bsp buildHenry Fuchs, Zvi Kedem and Bruce Naylor published binary space partitioning in 1980 for exactly this problem. Doom built the tree offline when a level was saved, so the game only ever read it.
- traverse that tree front to back from where you stand
bsp walkA BSP tree gives correct draw order for any viewpoint with no sorting at run time. Doom walked it near to far and kept a per-column clip range, so a far wall had nowhere left to draw.
- project a wireframe cube through the perspective divide
cubeDivide x and y by z and you have perspective. Everything above that in a 3D pipeline — matrices, clipping, culling — exists to deliver a usable z to that one division.
- move the eye back and flatten the cube toward orthographic
persp 8Perspective strength is the ratio between the near and far z values, not a zoom. Move the eye far enough away and the two are nearly equal, so the edges come out parallel.
- rotate the cube and count the multiplies it costs
rotate 35Rotating eight vertices costs about 32 multiplies. A 486DX2 at 66 MHz managed that easily. The expensive part was never the geometry, it was filling the pixels behind it.
- draw two intersecting quads with the painter sort and watch it fail
quadsThe painter algorithm draws whole polygons far to near. Two surfaces that pass through each other have no correct whole-polygon order, so it cannot be fixed by sorting harder.
- switch to a per-pixel depth test
zbuffer onQuake shipped in June 1996 with true 3D geometry, a z-buffer for moving objects and precomputed lightmaps for the static world. It wanted a Pentium, and it said so on the box.
- see both methods side by side on one screen
compareInterpolate 1/z across a triangle rather than z: 1/z varies linearly in screen space and z does not, which is the same reason perspective-correct texturing divides once per span.
- Boss missionrun the finished engine — textured, cosine corrected, shaded, fov 60, z-buffer on, camera moved
runWolfenstein 3D (May 1992) raycast a tile grid, Doom (December 1993) walked a BSP tree of sectors, Quake (June 1996) went fully 3D with a z-buffer. Four years separate this corridor from the first consumer 3D cards. The DOS era on this site runs the real Doom: type DOOM there and you are looking at the December 1993 renderer.
Certificate
This track is certifiable. Clear the boss mission in the terminal, then run EXAM SW3D for the written paper: 20 server-graded questions drawn from our own bank, pass mark 14 of 20. The certificate is issued once both are done, and it carries a verification code.