Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
114 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
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