//This tutorial is going to extend on the last heightmap tutorial //The difference here is that I am now adding lighting to the //terrain using a thing known as a normal. I have also added a little scale //feature to the terrain, so you can set the spacing between the vertices. //So lets get started shall we //-------------------------------------------------- //First Section: Variables //Now there are a few variables that I have added, some of them //are explained in the lighting tutorials. //This is the scale for the terrain, we can set //the distance between the vertices with this. This can //be used to create larger terrains. //float scale = 1; //here i am creating a struct for our normals //typedef struct //{ //float x; //the x position of the current normal //float y; //the y position of the current normal //float z; //the z position of the current normal //}NORMALS; //I have chosen to call them NORMALS - can be whatever you want //NORMALS ab; //normal for length AB - can be whatever you want //NORMALS ac; //normal for length AC - can be whatever you want //NORMALS n; //our final/main normal //here we set the materials for the terrain //GLfloat whiteDiffuseMaterial[] = {1.0, 1.0, 1.0}; //set the diffuse material to white //GLfloat greyAmbientMaterial[] = {0.25, 0.25, 0.25}; //set the shadows material to grey, the lower the number, the more intense the shadows //GLfloat whiteSpecularMaterial[] = {1.0, 1.0, 1.0}; //set the specular material to white //diffuse light color variables - 1,1,1 makes white //GLfloat dlr = 1.0; //GLfloat dlg = 1.0; //GLfloat dlb = 1.0; //ambient light color variables - 1,1,1 makes white //GLfloat alr = 1.0; //GLfloat alg = 1.0; //GLfloat alb = 1.0; //ligth position variables //GLfloat lx = -40.0; the x position //GLfloat ly = 100.0; the y position //GLfloat lz = -30.0; the z position //GLfloat lw = 0.0; //setting this to 0 sets the light as a diffuse light //-------------------------------------------------- //Section 2: Calculating the normals //here is our function to calculate the normals //with this you supply: //x1,y1,z1 - the points of the current vertex //x2,y2,z2 - the points of the vertex to the right //x3,y3,z3 - the points of the vertex to the left //each within the current shape. //void calculateNormals (float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3) { //double d; //d will hold the distance of each vector normal //get the length of the vector to the right //ab.x = x2 - x1; //ab.y = y2 - y1; //ab.z = z2 - z1; //get the length of the vector to the left //ac.x = x3 - x1; //ac.y = y3 - y1; //ac.z = z3 - z1; //here we do our dot product //this is something you learn about when going //over vectors. a dot product is equal to 0, and //is therefore perpendicular to the current vector. //n.x = (ab.y * ac.z) - (ab.z * ac.y); //n.y = (ab.z * ac.x) - (ab.x * ac.z); //n.z = (ab.x * ac.y) - (ab.y * ac.x); //here we get the distance of our normal vector //d = sqrt((n.x*n.x) + (n.y*n.y) + (n.z*n.z)); //then we normalize it by dividing the current //vector by its distance. //n.x = n.x / d; //n.y = n.y / d; //n.z = n.z / d; //} //-------------------------------------------------- //Section 3: Displaying the Heightmap with the Normals //To do this the first thing we have to do is add materials //to the object as we are using lighting now and our //color function doesn't manage lights. //Now in here you will notice tha I call calculate //normals for each vertex and then add a normal. //The hardest part here is working our which //number goes with which x,y and z. But once //you work it out, I believe the results //are pretty good. Not the best but //alot better then without shading. //void DisplayHeightMap (void) { //float Height; //set the materials for the heightmap //glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, whiteDiffuseMaterial); //glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT,greyAmbientMaterial); //glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, whiteSpecularMaterial); //for (mapX = 1; mapX < MapWidth; mapX +=4){ //for (mapZ = 1; mapZ < MapHeight; mapZ+=4){ //float Height1 = HeightMap[mapX][mapZ]; //float Height2 = HeightMap[mapX][mapZ+4]; //float Height3 = HeightMap[mapX+4][mapZ]; //float Height4 = HeightMap[mapX+4][mapZ+4]; // glBegin(GL_TRIANGLE_STRIP); // Height = HeightMap[mapX][mapZ]; // glTexCoord2f(0,0); //now we calculate our normals, we do this for each vertex // calculateNormals(mapX,Height1,mapZ,mapX+4,Height3,mapZ,mapX,Height2,mapZ+4); //then we add the normal // glNormal3f(n.x,n.y,n.z); // glVertex3f(mapX*scale,Height,mapZ*scale); // Height = HeightMap[mapX][mapZ+4]; // glTexCoord2f(0,1); //now we calculate our normals, we do this for each vertex // calculateNormals(mapX,Height2,mapZ+4,mapX,Height1,mapZ,mapX+4,Height4,mapZ+4); //then we add the normal // glNormal3f(n.x,n.y,n.z); // glVertex3f(mapX*scale,Height,(mapZ+4)*scale); // Height = HeightMap[mapX+4][mapZ]; // glTexCoord2f(1,0); //now we calculate our normals, we do this for each vertex // calculateNormals(mapX+4,Height3,mapZ,mapX+4,Height4,mapZ+4,mapX,Height1,mapZ); //then we add the normal // glNormal3f(n.x,n.y,n.z); // glVertex3f((mapX+4)*scale,Height,mapZ*scale); // Height = HeightMap[mapX+4][mapZ+4]; // glTexCoord2f(1,1); //now we calculate our normals, we do this for each vertex // calculateNormals(mapX+4,Height4,mapZ+4,mapX,Height2,mapZ+4,mapX+4,Height3,mapZ); //then we add the normal // glNormal3f(n.x,n.y,n.z); // glVertex3f((mapX+4)*scale,Height,(mapZ+4)*scale); // glEnd(); //} //} //} //And if you can work that all out, you should have a little idea on how //to use normals. if you are not sure of the math behind it. I will //be writing a tutorial on vector math in the forum, or you //can just google search it as normals are not just for opengl //they are for all 3D programs. #include #include #include #include #include #include int mapX; int mapY; int mapZ; const MapWidth = 384; const MapHeight = 384; float scale = 1; //the scale for the terrain *also new* //here i am creating a struct for our normals typedef struct { float x; //the x position of the current normal float y; //the y position of the current normal float z; //the z position of the current normal }NORMALS; //I have chosen to call them NORMALS - can be whatever you want NORMALS ab; //normal for length AB - can be whatever you want NORMALS ac; //normal for length AC - can be whatever you want NORMALS n; //our final/main normal //here we set the materials for the terrain GLfloat whiteDiffuseMaterial[] = {1.0, 1.0, 1.0}; //set the diffuse to white GLfloat greyAmbientMaterial[] = {0.25, 0.25, 0.25}; //set the shadows to grey, the lower the number, the more intense the shadows GLfloat whiteSpecularMaterial[] = {1.0, 1.0, 1.0}; //set the specular to white //diffuse light color variables GLfloat dlr = 1.0; GLfloat dlg = 1.0; GLfloat dlb = 1.0; //ambient light color variables GLfloat alr = 1.0; GLfloat alg = 1.0; GLfloat alb = 1.0; //ligth position variables GLfloat lx = -50.0; GLfloat ly = 100.0; GLfloat lz = -50.0; GLfloat lw = 0.0; //setting this to 0 sets the light as a diffuse light GLuint texture[10]; BYTE HeightMap[MapWidth][MapHeight]; GLuint LoadTextureRAW( const char * filename, int width, int height ); void FreeTexture( GLuint 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 ; } } } } //here is our function to calculate the normals //with this you supply: //x1,y1,z1 - the points of the current vertex //x2,y2,z2 - the points of the vertex to the right //x3,y3,z3 - the points of the vertex to the left //each within the current shape. void calculateNormals (float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3) { double d; //d will hold the distance of each vector normal //get the length of the vector to the right ab.x = x2 - x1; ab.y = y2 - y1; ab.z = z2 - z1; //get the length of the vector to the left ac.x = x3 - x1; ac.y = y3 - y1; ac.z = z3 - z1; //here we do our dot product //this is something you learn about when going //over vectors. a dot product is equal to 0, and //is therefore perpendicular to the current vector. n.x = (ab.y * ac.z) - (ab.z * ac.y); n.y = (ab.z * ac.x) - (ab.x * ac.z); n.z = (ab.x * ac.y) - (ab.y * ac.x); //here we get the distance of our normal vector d = sqrt((n.x*n.x) + (n.y*n.y) + (n.z*n.z)); //then we normalize it by dividing the current //vector by its distance. n.x = n.x / d; n.y = n.y / d; n.z = n.z / d; } void DisplayHeightMap (void) { float Height; //set the materials for the heightmap glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, whiteDiffuseMaterial); glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT,greyAmbientMaterial); glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, whiteSpecularMaterial); for (mapX = 1; mapX < MapWidth; mapX +=4){ for (mapZ = 1; mapZ < MapHeight; mapZ+=4){ float Height1 = HeightMap[mapX][mapZ]; float Height2 = HeightMap[mapX][mapZ+4]; float Height3 = HeightMap[mapX+4][mapZ]; float Height4 = HeightMap[mapX+4][mapZ+4]; glBegin(GL_TRIANGLE_STRIP); Height = HeightMap[mapX][mapZ]; glTexCoord2f(0,0); //now we calculate our normals, we do this for each vertex calculateNormals(mapX,Height1,mapZ,mapX+4,Height3,mapZ,mapX,Height2,mapZ+4); //then we add the normal glNormal3f(n.x,n.y,n.z); glVertex3f(mapX*scale,Height,mapZ*scale); Height = HeightMap[mapX][mapZ+4]; glTexCoord2f(0,1); //now we calculate our normals, we do this for each vertex calculateNormals(mapX,Height2,mapZ+4,mapX,Height1,mapZ,mapX+4,Height4,mapZ+4); //then we add the normal glNormal3f(n.x,n.y,n.z); glVertex3f(mapX*scale,Height,(mapZ+4)*scale); Height = HeightMap[mapX+4][mapZ]; glTexCoord2f(1,0); //now we calculate our normals, we do this for each vertex calculateNormals(mapX+4,Height3,mapZ,mapX+4,Height4,mapZ+4,mapX,Height1,mapZ); //then we add the normal glNormal3f(n.x,n.y,n.z); glVertex3f((mapX+4)*scale,Height,mapZ*scale); Height = HeightMap[mapX+4][mapZ+4]; glTexCoord2f(1,1); //now we calculate our normals, we do this for each vertex calculateNormals(mapX+4,Height4,mapZ+4,mapX,Height2,mapZ+4,mapX+4,Height3,mapZ); //then we add the normal glNormal3f(n.x,n.y,n.z); glVertex3f((mapX+4)*scale,Height,(mapZ+4)*scale); glEnd(); } } } void enable (void) { glEnable(GL_DEPTH_TEST); //enable the depth testing glDepthFunc(GL_LEQUAL); //set the depth function glFrontFace(GL_CCW); //set which face is facing forward glCullFace(GL_BACK); //set the face to be culled glEnable(GL_CULL_FACE); //enable culling to speed up the processing time glShadeModel (GL_SMOOTH); //set the shade model to smooth glEnable(GL_TEXTURE_2D); //enable texturing glEnable(GL_LIGHTING); //enable the lighting glEnable(GL_LIGHT0); //enable our diffuse light glEnable (GL_LIGHT1); //enable our ambient light glEnable(GL_NORMALIZE); //enable normalizing of normals, wont make a difference if disabled } void lights (void) { GLfloat DiffuseLight[] = {dlr, dlg, dlb}; //set DiffuseLight[] to the specified values GLfloat AmbientLight[] = {alr, alg, alb}; //set AmbientLight[] to the specified values glLightfv (GL_LIGHT0, GL_DIFFUSE, DiffuseLight); //change the light accordingly glLightfv (GL_LIGHT1, GL_AMBIENT, AmbientLight); //change the light accordingly GLfloat LightPosition[] = {lx, ly, lz, lw}; //set the LightPosition to the specified values glLightfv (GL_LIGHT0, GL_POSITION, LightPosition); //change the light accordingly } void display (void) { glClearColor (0.0,0.0,0.0,1.0); glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); enable(); lights(); //setup the lights glTranslatef(-50, -100, -50); //move to a position so we can see our terrain when rotated glRotatef(30, 1,0,0); //rotate to look down on the terrain glRotatef(150, 0,1,0); //rotate to put the terrain into view glBindTexture(GL_TEXTURE_2D, texture[0]); //bind our texture DisplayHeightMap(); //display our heightmap glutSwapBuffers(); } void init (void) { texture[0] = LoadTextureRAW("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 (60, (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; } GLuint LoadTextureRAW( const char * filename, int width, int height ) { GLuint texture[1]; unsigned char * data; FILE * 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[0] ); glBindTexture(GL_TEXTURE_2D, texture[0]); glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, GL_RGB, GL_UNSIGNED_BYTE, data); free( data ); return texture[0]; } void FreeTexture( GLuint texture ) { glDeleteTextures( 1, &texture ); }