My latest work (TerrainGL).

December 4, 2009 at 12:37 pm (OpenGL)

I’ve always dreamed of writing a terrain engine, probably since I was around thirteen years old.

Now that I’m so lucky to be attending to a computer graphics class, I made that dream come true by making it as my final project. This blog will introduce the methods I use to draw the terrain, and my progress from day one.

My goal is to write a massive terrain engine, of which can scale up a lot without slowing down your PC. The terrain height values are taken from a 24 bit bitmap, by the whiteness of the pixels. The darker the texel is, the lower the vertex is.

heightmap

Heightmap image

My first approach was very ugly, the terrain was drawn with GL_QUADS and it was also very slow. Next step was to draw it with GL_TRIANGLE_STRIP as it’ll require less vertices to draw each triangle.

GL_TRIANGLE_STRIP

GL_TRIANGLE_STRIP

Above, you may see that I only need 6 vertices to draw 4 triangles! When I was using GL_QUADS I needed 12 vertices, no sharing of vertices. Remember that sharing is always good! So you can see how I can dramatically decrease the vertices needed to draw triangles, meaning faster drawing and less OpenGL calls.

Here’s a preview of my first terrain:

Terrain

The texture on the terrain is repeated all over it, so it looks like a golf course. The lighting calculation is really easy, all the normals face up (0.0f, 1.0f, 0.0f). If I calculated it correctly for each face, the lighting would not go smooth over the neighbour surface. What do I do? After a little search on Google I found a neat way, thanks to http://www.lighthouse3d.com/opengl/terrain/index.php3?normals.

Let’s take a look at this picture:

Each surface has a normal, n1, n2, n3 and n4. We are now calculating the normal for the vertex v. That vertex is shared between n1, n2, n3 and n4. What we do is really simple, we take the sum of all the normals v = (n1 + n2 + n3 + n4) and the normal vector v will then give us a smooth lighting between the neighbor surfaces.

Next step was to create a shader, a shader that would color the terrain more brown/grayish on slope areas, and after a specific height I want the texels to become more white (snow effect on highest areas).

If I want to use a shader, the default OpenGL pipeline would be replaced with it, thus I would lose the fog effect, the texture on the landscape, lighting and pretty much everything.
The solution wasn’t that hard, I found a shader with the help of Google that introduces Phong shading, it’s a lighting technique similar to the OpenGL one except it’s much faster. I used the one from http://www.opengl.org/sdk/docs/tutorials/ClockworkCoders/lighting.php

To handle the textures in the shader, I simply added it to the phong shading calculation.

gl_FragColor.r = texel.r * gl_FragColor.r;
gl_FragColor.g = texel.g * gl_FragColor.g;
gl_FragColor.b = texel.b * gl_FragColor.b;

The problem I faced next was that I had little knowledge on how to pass vertex information to the shader, how much slope it has and so and forth. After a little search I managed to find a good way, I simply store the color values of the vertices using glColor! That way the vertex shader can read the gl_Color and pass it to the fragment shader where all the coloring magic will happen.

On the vertex shader I simply do:

vcolor = gl_Color;

That way I can pass it to the fragment shader and simply modify the RGB calculation a bit:

gl_FragColor.r = texel.r*gl_FragColor.r*vcolor.r;
gl_FragColor.g = texel.g*gl_FragColor.g*vcolor.g;
gl_FragColor.b = texel.b*gl_FragColor.b*vcolor.b;

Voila! Now I have phong shading that can render the texture and add color variations to the texels using glColor!

Results:

The landscape without slope and fog calculation involved.

Landscape with fog and slope calculation involved.

Here we can see the sunset!

Picture after writing a simple game and using MDL (Half-Life models) renderer.

The collision detection was really simple to do, for each surface made up of 2 triangles, I found the equation of the two triangles and solved y from x and z. I wrote a tutorial on that HERE.

Please do comment if you want the shader or any additional information!

Permalink Leave a Comment