Lighthouse3d.com

Please send me your comments
Terrain Tutorial

Index

Introduction

A TGA Library
A Simple TGA Library
TGA lib Source

Height Maps
Height Maps from Images

Lighting
Computing Normals
Simulating Lighting
Implementation Details
Screen Shots

Source Code

Artificial Terrain Generation
The Fault Algorithm
Implementation Details
Two Simple Variations
The Circles Algorithm

Mid Point Displacement
The MPD Algorithm

Particle Deposition

Smoothing
Smoothing
Matrix filters
API details

Source (Linux and Win32)

[Previous: Simulating Light] [Next: Screen Shots]

Terrain Tutorial


Simulating Lighting - Implementation Details


The terrain library has a set of functions to deal with this problem. The following variables were defined to store the required values.


    
static float terrainLightPos[4] = {0.0,0.1,0.1,0.0};
static float terrainDiffuseCol[3] = {1.0,1.0,1.0};
static float terrainAmbientCol[3] = {0.04,0.04,0.04};
static int terrainSimLight = 1;



First it is necessary to define the light's position (or direction for a directional light). OpenGL takes the light position as four float values. The last value defines wether the light is positional or directional.
  • 0 - directional light
  • 1 - point light
  • The first three values specify the lights position, in the case of a point light, or the lights direction for directional lights. The terrain lib has a function that takes as arguments four floats with the same meaning. The syntax is as follows:


    void terrainLightPosition(float x, float y, float z,float w);

    Parameters:
    x, y, z - the lights position or direction, depending on the type of light
    w - specifies the type of light: 0 (directional) or 1 (positional).


    The source code is presented next. Note that if the light is a directional light, the vector with the first three components is normalised to save time latter.


        
    void terrainLightPosition(float x, float y, float z,float w) {
    
    	terrainLightPos[0] = x;
    	terrainLightPos[1] = y;
    	terrainLightPos[2] = z;
    	terrainLightPos[3] = w;
    
    	/* if it is a directional light 
    	   normalise this vector to save time later 
    	*/
    	if (terrainLightPos[3] == 0.0)
    		terrainNormalize(terrainLightPos);
    }
    
    


    As mentioned in section Height Maps from Images, if the image has an alpha channel then the RGB components for each pixel are interpreted as vertex diffuse colors. However if the image has no alpha channel it is possible to define a diffuse color for the terrain. The next function can be used to set a color to the terrain. In this case all vertices will share the same color, which of course, will be altered by lighting. The syntax is as follows:


    void terrainDiffuseColor(float r, float g, float b);

    Parameters:
    r, g, b - the RGB color


    This function just copies the arguments to the terrainDiffuseCol array. The light's color is assumed to be white for the sake of simplicity.

    Vertices which are not lit will be completely black. In order to avoid this, an ambient component can be added to each vertex. This does not correspond to OpenGL lighting model, it is only a crude approximation, but the results are quite reasonable. The syntax is as follows:


    void terrainAmbientColor(float r, float g, float b);

    Parameters:
    r, g, b - the RGB color


    This function just copies the arguments to the terrainAmbientCol array.

    The next function tells the lib to simulate lighting. The syntax is as follows:


    int terrainSimulateLighting(int sim);

    Parameters:
    sim - 0 (do not simulate lighting) or 1 (simulate)


    The return value indicates if everything went smoothly. Because normals are required and the array may have not been created before, there is always the possibility of a memory allocation problem.


        
    int terrainSimulateLighting(int sim) {
    
    
    	terrainSimLight = sim;
    
    	/* just in case we don't have normals already */
    	if (terrainNormals == NULL) {
    		terrainNormals = (float *)
    			malloc(terrainGridWidth * terrainGridLength * sizeof(float) * 3);
    		terrainComputeNormals();
    	}
    	if (terrainNormals == NULL) 
    		return(TERRAIN_ERROR_MEMORY_PROBLEM);
    	else
    		return(TERRAIN_OK);
    
    }
    
    


    This is all the setup required. In an application using the terrain lib the following excert of code would perform the required initialization:


        
    	terrainSimulateLighting(1);
    	/* terrain will be light grey */
    	terrainDiffuseColor(0.9, 0.9, 0.9);
    	/* a small component of ambient color is added to prevent black vertices */
    	terrainAmbientColor(0.04, 0.04, 0.04);
    	/* a directional light over the terrain
    	terrainLightPosition(0,1,0,  0);
    
    


    As mentioned in section Height Maps from Images, the terrain lib has a function to create a display list using triangle strips. The actual code is not going to be presented in here.

    There are three possible situations for the vertex color:
  • simulate lighting with terrain colors from an image
  • simulate lighting with terrain colors defined by function terrainDiffuseCol
  • OpenGL lighting with terrain colors from an image
  • In the first two cases lighting needs to be computed. For each component the color is computed as:


        
    color = terrain color * factor + ambientColor
    
    


    where the factor is computed by the following static function:


        
    static float terrainComputeLightFactor(int i,int j,int offseti, int offsetj) {
    	
    	/* the offsets are only relevant in the positional light case */
    	
    	float factor,v[3];
    
    	if (terrainLightPos[3] == 0.0) /* directional light */
    	
    	/* factor = inner product between terrainLightPos and the normal vector */
    	factor = terrainNormals[3*(i * terrainGridWidth + j)] * terrainLightPos[0] +
    		 terrainNormals[3*(i * terrainGridWidth + j) +1] * terrainLightPos[1] +
    		 terrainNormals[3*(i * terrainGridWidth + j) +2] * terrainLightPos[2];
    	else { /* positional light */
    		/* compute unit lenght vector from the vertex to the light source */
    		v[0] = terrainLightPos[0] - (j + offsetj)*terrainStepWidth;
    		v[1] = terrainLightPos[1] - terrainHeights[i*terrainGridWidth + j];
    		v[2] = terrainLightPos[2] - (offseti -i) * terrainStepLength;
    		terrainNormalize(v);
    		/* factor = inner product between v and the normal vector */
    		factor = terrainNormals[3*(i * terrainGridWidth + j)] * v[0] +
    				terrainNormals[3*(i * terrainGridWidth + j) +1] * v[1] +
    				terrainNormals[3*(i * terrainGridWidth + j) +2] * v[2];
    	}	
    	/* check to see if factor < 0 */
    	if (factor < 0)
    		factor = 0;
    	return(factor);
    }
    
    


    [Previous: Simulating Light] [Next: Screen Shots]