Swiftless Game Programming Tutorials













Back to OpenGL Tutorial Index

Discuss this Tutorial in the Forum

This OpenGL tutorial is all about adding normals to your terrain to give the terrain a self shadowing system. You will notice with the previous one, if you add lighting and auto_normals, it will get darker the further away from the light the terrain is, but it wont differentiate between which faces are facing the light and which arent. This is all about adding specific normals to those faces to shadow your terrain.

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

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 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 ;
}
}
}
}

//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] = 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 (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;
}

Download C++ Source Code for this Tutorial

Download Visual Basic Source Code for this Tutorial (soon)

Download Heightmap (.RAW file)

Download Heightmap (.BMP file)

 
Site proudly supported by: http://www.cdadc.com
 

Want more information on Game Programming here? Just email me with your question.

CHOLESTEROL CHOLESTROL

cholesterolcholestrol.com Which covers CHOLESTROL / CHOLESTEROL as well as provides many fast WEIGHT LOSS tips and many diet recipes. Here are just a few of the pages covered in more detail:

Cholesterol:
Basics to prevent cardiovascular disease     Symptoms      LOWER CHOLESTEROL NATURALLY Foods, Vitamins, Herbs and Spices that reduce cholestrol     LOW CHOLESTROL, Low Calorie, Low Saturated Fat Recipes     FOOD CHART Cholesterol and saturated fats in foods     ALL ABOUT CHOLESTROL LEVELS Understanding cholestrol levels.     Sample Diet To lower cholesterol.     Menu planner Making a Low Cholesterol Diet much easier.    
Cholesterol and:
Turkey      Egg     Fish Oil and heart health      Red rice yeast     The OLD diet page     

Cholesterol Health, Fitness and Weight Loss Calculators:
FREE DAILY CALORIE INTAKE CALCULATOR, FAT INTAKE CALCULATOR, IDEAL WEIGHT CALCULATOR etc Goals for maximum health: Maximum and minimum weight, daily calorie / kilojoule intake, fat intake, saturated fat intake and, unsaturated fat intake, plus more.     WEIGHT LOSS CALCULATOR BASED ON BMI

REAL FAST WEIGHT LOSS

Index      Our Very Best Program I lost lots of weight, much from my stomach. I also lost my high cholesterol, pre-diabetes, metabolic syndrome and high blood pressure. This program really helped.     Exercise      Diet recipes page 1     Diet recipes page 2

(colesterol.cholesterolcholestrol.com Cuál cubiertas el COLESTEROL así como también proporciona muchas puntas rápidas de la PERDIDA del PESO y muchas recetas de la dieta.)

HEMORRHOIDS

HEMROIDS HEMORROIDS HEMOROIDS HEMEROIDS HEMMOROIDS HEMEROIDS All about them What they are etc, with photos included.     Hemorroides Spanish .     Causes     Bleeding     Home remedies     Treatment overview     Hemorrhoidectomy     Other surgery     Pregnancy and prolapses     Cure     Painful and or Thrombosed

DOWN SYNDROME

Jacob, a child with Down Syndrome and our fight to overcome Down Syndrome, Downs Syndrome, Down's Syndrome, Down Sindrome.     Glyconutrients     Intervention Increasing intelligence and abilities and reducing incapacity.     The Pregnancy Symptoms and Tests     Hypotonia     Altering the symptoms     Life Expectancy     Improving intelligence and lessening the retardation     Treatments     Causes

CHICKEN RECIPES

100's of them Recipes arranged by main word and utterly searchable.      Lots of whole chicken recipes from around the world.

GAME PROGRAMMING

COMPUTER 3D GAME PROGRAMMING TUTORIALS One of the best on the net.     Game Maker     Cube     camera     More camera code     Heightmap     Making fire in photoshop     Creating fog     Fullscreen     Lighting     Tutorials

HISTORY AND LEGENDS

HISTORY AND LEGENDS An extensive web site on the history and legends of Scotland and other places.     Urquhart and Glenmoriston Hundreds of pages on the history of Scotland, with particular reference to the Urquhart and Glenmoriston area.

Our MINOR WEB SITES - Our websites of few pages:

ALZHEIMER'S DISEASE     VASCULAR DEMENTIA     BIOLOGICAL TERRORISM and CLIMATE CHANGE     THE SCOTTISH CLAN URQUHART     DOCUMENTS OF REVOLUTION IDEAOLOGY    
But first, if you want to come back to this website again, just add it to your bookmarks or favorites now! Then you'll find it easy! Also, please consider linking to our site if you are a web master - reciprocal linking is fine.

If you have a page rank of 3 or more, we will be happy to engage a link exchange. Many people have been kind enough to link to us and this is our links page back to them to say thank you: Links We are currently able to offer up to 10 links from different websites and subdomains, in exchange for the same number from you - upto one for each of our web sites and subdomains in return.
Our Privacy Policy can be found at www.cholesterolcholestrol.com/privacypolicy.htm
Copyright © 2000-2007 Donald Shannon Urquhart. All Rights Reserved. Designated trademarks and brands are the property of their respective owners. Use of this Web site constitutes acceptance of our legal disclaimer.