Lesson 12: Terrain Rendering with OpenGL
In this tutorial we will render terrains. Essentially a terrain is a complex 3d model. It conists of vertexes, faces, texture coordinates, normal vectors etc. Al though it is possible to load complete models such as Wavefront OBJ models, it is generally better to dynamically generate them. First, we must define a terrain (or variation in heights) in some way. At the most basic level, we could define matrix M where every element of M is a height on the map. For example, we could define this matrix:
int M[8][8] ={ { 0, 0, 0, 0,0,0,0,0,0 }, { 1, 1, 1, 1,1,1,1,1,1 }, { 1, 3, 2, 3,3,4,2,1,1 }, { 1, 2, 1, 2,2,2,2,1,1 }, { 1, 2, 1, 2,2,2,2,1,1 }, { 1, 2, 1, 2,2,2,2,1,1 }, { 1, 2, 2, 3,3,3,3,2,1 }, { 1, 2, 2, 3,4,4,3,3,1 }, { 1, 2, 1, 1,1,1,1,1,1 }, { 0, 1, 1, 1,1,1,1,1,1 } };
And every element represents a height (the y coordinate of a vertex). The x and z coordinates of the vertexes are evenly spaced across the map. This map would result in the figure below. (Right: output image, Left: output image with added texture and lighting).
To draw this we would simply used a drawing code as:
int MAP_SIZE = 10; glPushMatrix(); for (int x = 1; xIf you want to use textures or face normals, you have to calculate them and add them here too
To draw in wireframe mode you can use:
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );This will work well for small terrains, but for bigger terrains this gets hard to maintain. E.g. if you want a larger terrain, the matrix M would quickly increase in size too. Not to mention all the manual typing of coordinates required. To solve this problem, we could use an gray scale image representing the heights of the map. Every pixel in this image represents the y-coordinate of a vertex on the map. The program would then load the image, convert each pixel to an element of height map M. Consider these examples:
You could render a more complicated terrain map using this technique:
To get a pixel value you can use this code:
Uint32 get_pixel(SDL_Surface *surface, int x, int y) { int bpp = surface->format->BytesPerPixel; /* Here p is the address to the pixel we want to retrieve */ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp; switch(bpp) { case 1: return *p; case 2: return *(Uint16 *)p; case 3: if(SDL_BYTEORDER == SDL_BIG_ENDIAN) return p[0]You may need to do normalization of the vectors and allocate enough memory (if you are using C/C++). Finally you would end up with something like:
if ((TextureImage[0] = SDL_LoadBMP("terrain.bmp"))) { for (x = 1; xThis is all theory required to do basic terrain generation, happy coding