OpenGL Terrain Generation – An Introduction

Introduction

NOTE: For the HTML version of this article with graphics and downloads please visit the following link:

http://www.codeproject.com/opengl/OPENGLTG.asp

I have always been interested in computer graphics and their applications. Data representation and visualization is one of the main areas of HCI (Human Computer Interaction), and the better you make the interaction between a machine and a human, the more productivity will be generated by both the human and the machine. I had some experience with OpenGL during my undergraduate studies while attending the California Polytechnic University. Unfortunately, I never got a chance to pursue the more advanced features of the OpenGL library, given my time and work responsibilities.

You can find more about OpenGL at http://www.opengl.org. There are also a bunch of good literature available on the topic of Computer Graphics and OpenGL that you can refer for further advancements. Please check the Background/Reference section for a list of some reference material that I have used, in general, for computer graphics.

The following project is a very simple example demonstrating how to generate a terrain based on a bitmap file. The objective of the project is to generate a three dimensional terrain based on some data file. Please note, that this could have been any data file, but for the purpose of our example, we are going to be using a 32×32 dimensional bitmap file. We could have easily used a text file, and defined logic for each word or letter to represent it graphically.

The project also contains a good Windows framework that can be used for your other OpenGL applications. The current project allows you to rotate the camera using your mouse.

Once again, this is a simple approach to terrain generation, which can be a very difficult task in complex environments.

Background/Reference

Since Computer Graphics is kind of an advanced topic, it is necessary to have at least some king of understanding and exposure to the concepts and theories in the field. However, this does not mean that you will not be able to use the following code or understand it. I have made it as simple as possible, and hopefully, it will give you a good start, or some additional source of information that you can use for your projects. Also, please note that you will need to have a good understanding of C/C++ programming.

Some books I have used for learning Computer Graphics and OpenGL programming:

Books I used while attending the California Polytechnic University:

OpenGL Programming Guide, or better know as the Red Book.

Computer Graphics Using OpenGL, 2nd Edition.

Books I used while attending the California Lutheran University:

OpenGL: A Premier, 2nd Edition.

Interactive Computer Graphics: A Top-Down Approach Using OpenGL, 4th Edition.

What is a Terrain?

Some background information on a terrain and their uses in a game application: A terrain in an environment is one of the most critical components in the scene that is being rendered. It could easily be the largest 3D object in the project. Rendering the terrain can become a daunting task, taking the most time to render in a scene. To keep the terrain engine running in real time can be a difficult task, and it requires some thought out processes and modeling for it to be sufficient.

To be effective, the terrain needs to meet a number of requirements, many of which can be contradicting each other. A terrain should appear to be continuous to the end user, yet the mesh should be simplified or culled where possible, to reduce the load on the graphics card.

In a gaming system, for example, some engines draw the terrain just beyond the point a player can reach, and then use a terrain drawn onto a skybox to simulate hills or mountains in the distance. The terrain should appear realistic to the setting for the environment, yet this can be taxing on the video card, and a balance needs to be maintained. Detail textures are often used close to the camera, allowing the areas further off to be rendered more quickly.

What is a Height Map?

The first thing required for terrain rendering is a representation of the terrain’s shape. While there are a number of structures that can be used to perform the job, the most widely used is the height map. Others include: NURBS, which can be maintained through a number of control points, and voxels, which allow for overhangs and caves.

There is one drawback to a height map, and that is, for every point on the XZ-plane, there can only be one height value. You can see that this limits the representation of overhangs and caves by a height map. This can be overcome by using two separate models.

Another drawback is that height maps take a large amount of memory, as each height must be represented. On the other hand, height maps lend themselves to the creation of regular meshes easily. It is also easy to determine the height at any given location, which is useful for collision against the terrain as well as laying dynamic shadows onto the terrain.

A height map is represented by a 2D array of values, where for any point (X, Y, Z), the X and Z are the indexes into the array, and the value of the array is the Y value which is equivalent to the height value.

The following is an example of such a representation:

int height[5][5] ={

{ 0, 0, 1, 1, 2 },

{ 0, 1, 2, 2, 3 },

{ 0, 1, 3, 2, 3 },

{ 0, 1, 2, 1, 2 },

{ 0, 0, 1, 1, 1 } };

The Approach!

There are many advanced algorithms to generate terrains; I am using a very simple solution for the purpose of this project.

In a nutshell, I used a 32 x 32 grayscale bitmap to represent a height-field that is used to generate the terrain. The terrain itself is divided into a grid of height values, the result of which is a mesh representing the terrain in the scene.

We create a grid of vertices that are spaced evenly apart but have varying heights, based on the height-field data. The color value of each bit is used to determine the height value of each grid location; in this case, for a 24-bit grayscale bitmap, the values for the color range from 0 to 255.

Once the bitmap has been read and the values loaded in memory, we have the data needed to represent the terrain. We also use a variable called a MAP_SCALE to allow us to scale the map up or down. This is a scale factor; we use this to set the distance between each height vertex. This allows us to increase or decrease the size of the terrain.

When we actually assign the vertex coordinates for each grid location, we need to apply the MAP_SCALE factor, which is multiplying it with the grid location index based on the coordinate element, i.e.:

Terrain[X][Z][0] = Float(X)*MAP_SCALE;

Terrain[X][Z][1] = (float) imageData[(Z*MAP_SCALE+X)*3];

Terrain[X][Z][2] = Float(Z)*MAP_SCALE;

The terrain map is represented in a grid of height values, which is internally stored in a 2D array of vertex coordinates. It extends along the X and Z-axis, with the Y-Axis representing the terrain height.

To render the terrain map, we use GL_TRIANGLE_STRIP for each row of grid values along the Z-axis. To render the terrain correctly, we need to specify the point in a specific order.

This requires us to start at the end of the row and move along the positive X-axis by drawing the vertices in a Z pattern:

(*)==========>(*)

/

/

/

/

/

/

/

/

/

/

/

(*)=========>(*) ….

Using the Code

I will only list the code that deals with the terrain generation here. There is more code in the project that you can look at. It is well documented, so you shouldn’t have any problems. The solution was compiled using MS Visual Studio 2003, so you should be able to compile and run it easily. You will need to have the OpenGL libraries and DLL, which I will also provide as a download option just in case you do not have them. Make life a little easier so you do not have to search for them online.

So the majority of the code is for preparing the windows to render the scene properly. As you can see below, the code for generating and rendering the terrain is very short. To give an overview, the first thing that happens is for the windows to get created, and then we initialize OpenGL, and read in the BMP file and assign it to the 2D array we discussed above. Then, the texture is applied to the surface of the mesh, and the scene rendered to the screen.

Without further ado, the following listing is the the portion of the code which initializes and generates the terrain:

.

.

.

// InitializeTerrain()

// desc: initializes the heightfield terrain data

void InitializeTerrain()

{

// loop through all of the heightfield points, calculating

// the coordinates for each point

for (int z = 0; z http://www.nicksoftware.com