| |
 |
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:
|
 |