Landscape rendering in less than 20 lines of code
- Project demo page
Let us go back to the year 1992. Processing power were 1000 times slower and acceleration via a GPU were unknown or unaffordable. 3D games graphics used very simple rendering algorithms and showed mostly polyons with a simple color.
It was during that year Novalogic published the game Comanche.
The graphics were awesome and in my opinion 3-5 years ahead of its time. You see a lot more details, shading and even shadows. And all this with the same processing power as other 3D games.
Comanche uses a technique called voxel space similar to raycasting.
To display the landscape a 10241024 one byte height map and a 10241024 color map is used which you can download on this site. These maps are periodic:
The algorithm draws just vertical lines. The following figure demonstrate this technique.
- Clear Screen.
- For visible surface determination start from the back and render to the front
- Determine the line on the map, which corresponds to the same optical distance from the observer. Consider the field of view and persective correction.
- Segment the line so that it matches the number of columns of the screen.
- Load the height and color from the 2D maps corresponding of the segment of the line.
- Do some perspective corrections for the height coordinate.
- Draw a vertical line with the corresponding color with the height retrieved from the perspective correction.
The core algorithm contains in its simplest form only a few lines of code:
def Draw(p, height, horizon, screen_width):
# Draw from back to the front (high z coordinate to low z coordinate)
for z in range(240, 1, -1):
# Find line on map. This calculation corresponds to a field of view of 90°
pleft = Point(-z + p.x, -z + p.y)
pright = Point( z + p.x, -z + p.y)
# segment the line
dx = (pright.x-pleft.x) / screen_width
# Draw vertical line for each segment
for i in range(0, screen_width):
DrawVerticalLine(i,
(height - heightmap[pleft.x, pleft.y]) / z * 120. + horizon,
colormap[pleft.x, pleft.y])
p1eft.x += dx
# call the drawing function with position, height, horizon line and screen width parameter
Draw( Point(0, 0), 50, 120, 700 )
There are of course a lot of tricks to get higher performance not mentioned here.