Swiftless Game Programming Site
Home Tutorials Contact
Home
News
OpenGL
GLSL
OpenCL
Maths
Misc
Work in Progress
Resume
 
 

Load in a heightmap in OpenGL

If you would like to see this site updated, please help out and

Well this is gonna be quite the little tutorial. In this opengl tutorial I will be teaching you how to create opengl terrain using heightmaps. You will be learning how to load a RAW file as a heightmap and also texturing with heightmaps. You will also get an idea on how opengl terrain generation and opengl terrain rendering works.

Well this is gonna be quite the little tutorial

--------------------------------------------------
First section: Variables

In this application, we use the variables:
int mapX;
int mapY;
int mapZ;
To hold our X,Y and Z coordinates for our heightmap
during the application.

We use:
const MapWidth = 384;
const MapHeight = 384;
To hold the width and height(length) for our heightmap
these have to be 3 times the size of the width and height
of our RAW file as our RAW file is parsed in intervals
of 3. So our 128 X 128 heightmap is really 384 X 384.

We then set up our texture storage space with:
GLuint texture[10];
I have set up enough storage for 11 textures
for the next installment of terrain generation.

Now the next variable holds all our heights with:
BYTE HeightMap[MapWidth][MapHeight];
I have set it to BYTE as all our data from our RAW
file is read in BYTES as Binary.

---------------------------------------------------
Section 2: Loading the HeightMap Heights

Our first function is called LoadHeightMap
with arguments: (char* Filename, int Width, int Height)
this will be called on startup to read in our HeightMap
and allocate the data accordingly in our HeightMap array.

Within this I have the variable int smoothen, this will be
used later on in a loop to parse the heights and average
them amongst there surrounding heights for smoother
looking terrain.

We also have the variable FILE *File which will be the
file we are opening (heightmap.raw).

Next we set our File to our currently selected filename
and read it with the properties "rb" meaning read-binary.

Then we acctually read the file into our Heightmap array
in intervals of 1 byte per array for as many times as
our files width X height X 3 (remembering our RAW file
is parsed in intervals of 3).

Now that we have all the data within our array, we close
the file.

Next comes the smoothing, to smooth, I have gone through
every height within our array and averaged it out amongst
its surrounding heights. Simple addition and division to
get the average. I have parsed this through 3 times to get
an alright looking terrain.

---------------------------------------------------
Section 3: Displaying the HeightMap

To get the optimum look out of the terrain, I am loading every
fourth height and setting them 4 units apart. If I set them
1 unit apart and call every 1 then the terrain looks jagged.
And if I set them further apart and call less, then it looks
too smooth for me. So you can change these as you see fit.

Now acctually displaying them is simple enough. I am using triangle
strips and am calling a triangle strip for every width and height.
Within this I am also setting our texture coordinates. Self explanatory
if you know about triangle strips.

---------------------------------------------------
Section 4: Optimization

To optimise our terrain to get the best amount of frames
per second, I have added some Culling in the enabling
code to discard faces we dont need (remember to set the front face to CCW
- counter clock wise -as triangle strips set there faces opposite to most
shapes).

Also by calling only every fourth height, I am saving the processing
times and thus also increasing the frames per second.

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
  #include <GL/gl.h>
#include <GL/glut.h>
#include <windows.h>
#include <stdio.h>
#include <math.h>
#include <iostream.h>

int mapX;
int mapY;
int mapZ;

const MapWidth = 384;
const MapHeight = 384;

GLuint texture[10];

BYTE HeightMap[MapWidth][MapHeight];

GLuint LoadTexture( const char * filename, int width, int 
height )
{
    GLuint texture;
    unsigned char * data;
    FILE * file;

    //The following code will read in our RAW file
    file = fopen( filename, "rb" );
    if ( file == NULL ) return 0;
    data = (unsigned char *)malloc( width * height * 3 );
    fread( data, width * height * 3, 1, file );
    fclose( file );

    glGenTextures( 1, &texture ); //generate the texture with 
the loaded data

    glBindTexture( GL_TEXTURE_2D, texture ); //bind the texture
 to it's array

    glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, 
GL_MODULATE ); //set texture environment parameters

    //here we are setting what textures to use and when. The MIN 
filter is which quality to show

    //when the texture is near the view, and the MAG filter is which
 quality to show when the texture

    //is far from the view.

    //The qualities are (in order from worst to best)
    //GL_NEAREST
    //GL_LINEAR
    //GL_LINEAR_MIPMAP_NEAREST
    //GL_LINEAR_MIPMAP_LINEAR

    //And if you go and use extensions, you can use Anisotropic 
filtering textures which are of an

    //even better quality, but this will do for now.
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
 GL_LINEAR_MIPMAP_LINEAR );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
 GL_LINEAR_MIPMAP_LINEAR );

    //Here we are setting the parameter to repeat the texture 
instead of clamping the texture

    //to the edge of our shape. 
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 
GL_REPEAT );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 
GL_REPEAT );

    //Generate the texture with mipmaps
    gluBuild2DMipmaps( GL_TEXTURE_2D, 3, width, height, 
GL_RGB, GL_UNSIGNED_BYTE, data );
    free( data ); //free the texture
    return texture; //return whether it was successfull
}

void FreeTexture( GLuint texture )
{
  glDeleteTextures( 1, &texture );
}

void LoadHeightMap (char* Filename, int Width, int Height) {

int smoothen;

FILE * File;

File = fopen( Filename, "rb" );

fread( HeightMap, 1, Width * Height * 3, File );

fclose( File );

for (smoothen = 0; smoothen < 3; smoothen++){
for (mapX = 1; mapX < Width; mapX ++){
for (mapZ = 1; mapZ < Height * 3; mapZ++){
mapY = HeightMap[mapX][mapZ];
mapY += HeightMap[mapX-1][mapZ];
mapY += HeightMap[mapX+1][mapZ];
mapY += HeightMap[mapX][mapZ-1];
mapY += HeightMap[mapX][mapZ+1];
mapY += HeightMap[mapX-1][mapZ-1];
mapY += HeightMap[mapX-1][mapZ+1];
mapY += HeightMap[mapX+1][mapZ-1];
mapY += HeightMap[mapX+1][mapZ+1];
mapY = mapY/9;
HeightMap[mapX][mapZ] = mapY ;
}
}
}
}

void DisplayHeightMap (void) {
int Height;

for (mapX = 1; mapX < MapWidth; mapX +
=4){
for (mapZ = 1; mapZ < MapHeight * 3; mapZ+=4){
    glBegin(GL_TRIANGLE_STRIP);
    Height = HeightMap[mapX][mapZ];
    glTexCoord2f(0,0);
    glVertex3f(float(mapX),Height,float(mapZ));
    
    Height = HeightMap[mapX][mapZ+4];
    glTexCoord2f(0,1);
    glVertex3f(float(mapX),Height,float(mapZ+4));

    Height = HeightMap[mapX+4][mapZ];
    glTexCoord2f(1,0);
    glVertex3f(float(mapX+4),Height,float(mapZ));

    Height = HeightMap[mapX+4][mapZ+4];
    glTexCoord2f(1,1);
    glVertex3f(float(mapX+4),Height,float(mapZ+4));
    glEnd();
}
}
}

void enable (void) {
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glFrontFace(GL_CCW);
    glCullFace(GL_BACK);
    glEnable(GL_CULL_FACE);    
    glShadeModel (GL_SMOOTH);
    glEnable(GL_TEXTURE_2D);
}

void display (void) {
    glClearColor (0.0,0.0,0.0,1.0);
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    enable();
    glTranslatef(30, 0, 30);
    glRotatef(150, 0,1,0);
    glColor3f(1,1,1);
    glBindTexture(GL_TEXTURE_2D, texture[0]);
    DisplayHeightMap();
    glutSwapBuffers();
}

void init (void) {
    texture[0] = LoadTexture("texture.raw", 256, 256);
    LoadHeightMap ("height.raw", 128, 128);
}

void reshape (int w, int h) {
    glViewport (0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    gluPerspective (100, (GLfloat)w / (GLfloat)h, 0.01, 500.0);
    glMatrixMode (GL_MODELVIEW);
}

int main (int argc, char **argv) {
    glutInit (&argc, argv);
    glutInitDisplayMode (GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA
);
    glutInitWindowSize (500, 500);
    glutInitWindowPosition (100, 100);
    glutCreateWindow ("A basic OpenGL Window");

    init();

    glutDisplayFunc (display);
    glutIdleFunc (display);
    glutReshapeFunc (reshape);

    glutMainLoop ();

    return 0;
}

Download C++ Source Code for this Tutorial

Download Visual Basic Source Code for this Tutorial

Download Heightmap (.RAW file)

Download Heightmap (.BMP file)


Comments:

Name   Email

Reload Image

 
     

 

Copyright 2009, Donald Urquhart AKA Swiftless
Check out: http://www.cdadc.com