In this article, I give a brief introduction to heightmaps before looking at some basic ways to color and texture a heightmapped terrain. I try to expose the weaknesses of these methods before going on to look at a system which allows realistic, high-detail textures to be used for many different ground types.
The real basics - definitions and my conventions
A heightmapped terrain is normally represented by a 2D grid of points, each having a height. The basic terrain is drawn by rendering 3D triangles using these vertices:
In this example we have a 4x4 grid - there are 16 points and 9 'tiles'. Note that an NxM grid will have (N-1)x(M-1) 'tiles'. Although in the view shown the grid looks like a 2D tiled surface, the fact that each vertex has a height means we have to draw two triangles for each tile, as shown by the diagonal lines. My convention is to label the bottom-left point of the grid (0,0) with x increasing to the right and y increasing up; I always split the tiles as shown. You could also do it like this:
This representation of the terrain makes rendering the geometry of the landscape very simple - you just draw each quad, or pair of triangles, with the correct heights. It also makes creating/editing heightmaps simple. You can use images as a source of height data and thus create your terrain in MSPaint (!), or make an editor to set these data points instead. Rendering your heightmap might now look something like this:
The next thing you obviously want is to make the terrain more life-like by adding colors/textures to it. The simplest thing is to apply colors. You can assign a color at each vertex, or encode it into a texture which is stretched to cover the whole map. These options both require the minimum of Direct3D / OpenGL knowledge and are a good thing to do when starting your project, but they're never going to cut it for making interesting graphics:
While at long distance the terrain looks detailed enough (the map is colored at a 1mx1m resolution here) the lack of fine detail makes the foreground bland, and it's hard to see what's there. There is one small extra step you can make to get your landscape looking at least a little interesting however - adding a detail texture...
Basic Terrain Texturing
Let's assume you have a renderer which draws the polygons for a heightmap, and also stretches a texture across the whole map. Size considerations mean this texture can only give basic color information - if you have a 1000x1000 heightmap representing 1Km square, a 4096x4096 texture will only give a resolution of .25m - it's just not possible to get enough detail onto the ground this way. So we'll just use this colored texture to provide the general color - greens for vegetation, browns for mud etc. Now to get detail, we apply another texture to the map. But we don't stretch the texture across the map - we repeat it many times. This texture might look a bit like static on the TV - a pretty random grayscale image. If we tile it once per metre in both directions, our terrain will be very detailed at close range but extremely monotonous in general. However if we apply this as well as the stretched colored texture, we get terrain which has large scale detail suggesting mud, grass, snow etc but is also detailed at close range so your eyes can get something to focus on. This technique is seen quite a lot in terrain demos because it's so easy, and as such is a good milestone to have in your progress - the first reasonably interesting representation of your landscape, especially with decent lighting and shadows. But if you've made a terrain renderer along these lines you'll soon see that it's still not particularly riveting, as the next image illustrates:
Lots of other objects like cars, buildings etc can mask this but the fact that all your ground has the same appearance close up isn't realistic. While old games did things like this, coloring some bits of the map brown or green doesn't fool anyone these days! What we need is variation in the way different ground types (grass, sand...) appear at close range, rather than just their base colors...
Introducing ground types
In a general sense, what we need is pretty obvious. Each ground type - like grass #4, mud#23, tarmac etc needs its own detailed texture, which actually looks like the material being modelled. These could all be greyscale, blended with one large texture for the whole map which supplies the color, or we could dispense with that and simply have several high-detail colored textures for the different ground types we wish to have in our maps. The question we have is how to represent which bits of the map use which ground types, and the obvious answer is to tie this extra information to the vertices.
As a first try, it seems logical to continue with the 2D tiled approach and set each vertex with a ground type.
In this illustration each tile is textured using the ground type for its bottom left corner vertex: (0,0), (2,0) & (1,2) use the green ground type, (1,0), (0,2) & (2,2) use the blue ground type and (0,1), (1,1) & (2,1) have the yellow ground texture assigned.
This system really isn't much more complicated to implement than the detail texturing already covered, although performance issues start to arise deciding what order to draw things in. Unfortunately, it looks probably worse than using the single detail texture method. Below are screen shots illustrating this method:
Although we now get the benefit of more realistic ground textures (despite the poor quality of those used here you can clearly differentiate the grey pebbles from the other textures) this is negated by the very obvious seams between different ground types. Before we go on to look at what I did to get around this problem, here's screenshots of the same scenes - in the current version of my editor: