Tuesday, August 17, 2010

Terrain - Getting started

It's time to start some real work now. :)

This time a big part of my game design gets revealed because as the title indicated the game will be played outdoors and I'll need a terrain for that.

To be more precise I need a terrain that gets generated based on a height map. The terrain will be a large grid consisting of vertices. Each one of these vertices will correspond to a pixel on the height map and the height of this vertex is derived from the color (e.g. red channel) of that pixel. See picture below for alignment of vertices and triangles of this grid.

Terrain Grid
(from an ortho cam looking down)

If we take a look at the grid - and to its structure - we can notice two things that we can use to make rendering more efficient:
  • every vertex (that is not on the border) is shared by 6 triangles! This makes this grid a very good candidate for using vertex indices. This way every "logical" vertex on the grid will have only one corresponding physical vertex in the vertex buffer. And different triangles "using" the same physical vertex will refer to it with an index. Why is it good? Because the size of a vertex is big (contains position, normal, texture uv, etc) but size of an index is small (an int or short).
  • it is very easy to find a triangle sequence where the (n+1)th triangle shares a side with the nth triangle. Thus this grid is a good use case for using TriangleStrip as primitive type. This way we need only k+2 indices to render k triangles (instead of needing k*3 indices in case we would use the TriangleList primitive type).

Let's create our height map. One can realize it quickly that if we want to have good looking hills and valleys then we should get a painting tool capable of drawing gradients. I have one but still: it is cumbersome to draw a height map that will result in a realistic terrain - that is, in a terrain that doesn't look stupid.

Therefore I was simply searching for 'heightmap' on google images. I picked this one:

375x375 height map

To check out the terrain let's use a BasicEffect first to render it. First thing I noticed that terrain without lighting isn't very impressive.

Terrain rendered without lighting enabled

To enable lighting on BasicEffect is quite simple (basicEffect.EnableDefaultLighting()). But to make it work we need a normal vector to be set for every vertex. To calculate the normal of a vertex I do following for every vertex:

  • get all triangles connected to the vertex
  • calculate normal vectors for all of these triangles
  • average these normal vectors and set the result as normal vector of our vertex 

The result looks much better:

Terrain rendered with lighting

This is it. A simple height map generated, textured terrain.

One note on performance, though: Because the terrain is static - non of the vertex data changes - we can achieve better performance by copying vertex (and index) data to the graphics card. This way vertex data travels to GPU from video RAM and not from system RAM - which results in a much higher performance.

To do this I use Microsoft.Xna.Framework.Graphics.VertexBuffer (and IndexBuffer) instances which are representations of (vertex or index) lists on the graphics device.

To see the terrain in action take a look at the video:

No comments:

Post a Comment