Upcoming Events
Southwest Gaming Expo
11/20 - 11/22 @ Dallas, TX

Workshop on Network and Systems Support for Games (NetGames 2009)
11/23 - 11/25 @ Paris, France

ICIDS 2009 Interactive Storytelling
12/9 - 12/11 @ Guimarães, Portugal

Global Game Jam
1/29 - 1/31  

More events...


Quick Stats
7223 people currently visiting GDNet.
2341 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!



Link to us

Link to us

  Intel sponsors gamedev.net search:   

Ray Lighting: Creating realistic shadows for terrain


3. Implementation

3.1. Variables

///////////////
/* Variables */
///////////////

// Tells if there is a shadow.
bool Shadow;

// Holds the coordinates for the shadow greyscale.
int ShadowOriginX;
int ShadowY;

// Hold the values for the rays' height and the terrain-maps' height.
float RayY;

// Coordinate positions variables. int RayOriginX, MapX, z, MapY; // The angle in which the rays intersect the x-axis. int Angle = 45; // The max height on the terrain-map. int MaxMapHeight = 255; // The max shadow brightness and. int MaxShadowBrightness = 255; // The size of the terrain int MapSize = 128; /* Pre-calculations */ // Get the tan value for the angle that is used. float TanAngle = float( tan(Angle*3.14159/180) ); // Calculate the maxHeightExtension int MaxHeightExtension = int(MaxMapHeight/TanAngle); // Set all points on the lightmap MaxShadowBrightness for (z = 0; z < MapSize; z++){ for ( MapX = 0; MapX < MapSize; MapX ++){     LightMap[MapX][z] = MaxShadowBrightness;   } }

3.2. Shadow Calculations

/////////////////////////
/* Shadow Calculations */
/////////////////////////

/* Ray Lighting */

// Loop through the z-axis.
for ( z = 0; z < MapSize-1; z++ ){

  // Loop through the x-axis.
  for ( RayOriginX = MapSize-1; RayOriginX > -(MaxHeightExtension); RayOriginX-- ){

    // Reset the shadow values.
    ShadowOriginX = 0;
    Shadow = false;

    // If the shadow's maximum width exceeds the mapsize set MapX to mapsize.
    if (RayOriginX + MaxHeightExtension +1 > MapSize){

      MapX = MapSize;

    }

    // Else set MapX the shadow's maximum width.
    else{

      MapX = RayOriginX + MaxHeightExtension +1;

    }

    // Loop through the ray from the shadows maximum width until it
    // reaches the rays' origin.
    while( MapX > RayOriginX && MapX > 0){

      // Get the height values.
      RayY = float( MapX-RayOriginX * TanAngle );
      MapY = HeightMap[MapX][z];

      // If the MapY intersect the Ray there will be a shadow.
      if ( RayY <= MapY){

        // Set the shadows' origin X coordinate.
        ShadowOriginX = MapX;

        // There will be a shadow.
        Shadow = true;

      }
      // Else if MapY is lower than RayY and there is a shadow.
      else if ( Shadow ){

        // Get the shadow greyscales height.
        ShadowY = HeightMap[ShadowOriginX][z];

        // Calculate and set the brightness.
        LightMap[MapX][z] = unsigned char( 255 * (float(ShadowOriginX-MapX)/
                            (ShadowOriginX-RayOriginX) + MapY/(float)ShadowY) );

      }

      MapX--;
    }
  }
}

/* Blurring */

// Loop through the z-axis.
for ( z = 0; z < MapSize-1; z++){

  // Loop through the x-axis.
  for ( RayOriginX = 0; RayOriginX < MapSize-1; RayOriginX++){

    // If the point isn't already lighted.
    if (LightMap[RayOriginX][z] != MaxShadowBrightness){

      // Get the mean value of vertex and set the brightness.

      LightMap[RayOriginX][z] = ( LightMap[RayOriginX-1][z+1]  +
      LightMap[RayOriginX][z+1] + LightMap[RayOriginX-1][z]    +
      LightMap[RayOriginX][z]   + LightMap[RayOriginX-1][z-1]  +
      LightMap[RayOriginX][z-1] ) /6;

    }
  }
}

3.3. Colouring the Light Source

To simulate a warm red sunset or add a bit of a mystic blue light to a scene the brightness values have to be modified. The shadows becomes coloured by multiplying the brightness value for red, green and blue with the specified amount of colour. Using the equation Intensity = brightness * colour, the colour values are sent to the API:

ColorToAPI ( (GetBrightnessAtPoint( x, z ) * Red  ),
             (GetBrightnessAtPoint( x, z ) * Green),
             (GetBrightnessAtPoint( x, z ) * Blue ) );

3.4. Horizontal Rotation

By inverting the coordinates the light source will be rotated. To rotate the light source 180 degrees and set the light direction X+, calculations of RayOriginX start from MapSize towards 0 and MapX from 0 towards RayOriginX.

for ( RayOriginX = -(maxHeightExtension); RayOriginX < MapSize-1; RayOriginX ++ ){

  for ( MapX = RayOriginX; MapX < RayOriginX + maxHeightExtension+1 && MapX > 0; MapX++ ){

To calculate in the direction of Z+ or –Z the x and z variables have to be exchanged.

MapY = HeightMap[z][MapX];

ShadowY = HeightMap[z][ShadowOriginX];

LightMap[z][MapX] = 200 * ( float(shadowX-x2)/float(shadowX-x1)+ MapY/ShadowY );

This will allow the light source to rotate 90 degrees horizontally. However this is not the best way to do it. The heightmap and textures may be rotated and the shadows then calculated which would give the same results as rotating the light source.

3.5. Optimizations

Without soft-shading some optimizations are possible that will sacrifice quality for performance. Faster calculations may decrease the initialization time of the application.

/* Optimized Ray Lighting */

// Pre set the brightness.
Brightness = 200;

// Loop through the z-axis.
for ( z = 0; z < MapSize-1; z++){

  // Loop through the x-axis.
  for (RayOriginX = MapSize-1; RayOriginX > -(MaxHeightExtension); RayOriginX --){

    // Reset the shadow value.
    Shadow = false;

    // If the shadows maximum width exceeds the mapsize set MapX to mapsize.
    if (x1+ MaxHeightExtension +1 > MapSize){

      MapX = MapSize;

    }

    // Else set MapX the shadow's maximum width.
    else{

      MapX = x1 + MaxHeightExtension +1;

    }

    // Loop through the ray from the shadows maximum width until it
    // reaches the rays' origin.
    while(MapX > RayOriginX && MapX > 0){

      // Get the height value.
      RayY = float((MapX-RayOriginX) * TanAngle);

      // If the MapY intersect the Ray there will be a shadow.
      if ( RayY <= heightmap[MapX][z]){

        // There will be a shadow.
        Shadow = true;

        // If all the previous points are already shaded and there will be a
        // shadow then set MapX the previous point.
        if (lightmap[RayOriginX+1][z] == 150){

          MapX = RayOriginX +2;

        }
      }

      // Else if MapY is lower than RayY and there is a shadow.
      else if (Shadow){

        lightmap[MapX][z] = Brightness;

      }

      MapX--;

    }
  }
}




Results


Contents
  Introduction
  Implementation
  Results

  Printable version
  Discuss this article