L3DT documentation Large 3D terrain generator

# Light mapping algorithms

## Overview

The L3DT lighting routine includes the following features:

• Different RGB spectra for direct and ambient lighting, allowing afternoon sun or full moon effects.
• Anisotropic ambient lighting (discussed below).
• Physically realistic light absorption by water.
• Light refraction in water (shadows, lighting directions, etc).
• Bump mapping.

## Calculating light intensity

The RGB intensity of a pixel in the light map is given by:

Irgb = Sunrgb × N·v + Ambrgb × (Nz + 1) ÷ 2

Where:

• Sun = the RGB intensity (spectrum) of the sun / moon / whatever.
• Amb = the spectrum of ambient light.
• I = the resulting RGB intensity on the light map pixel.
• N = the normal vector from the surface (unit length).
• Nz = is the vertical component of the normal vector (from the normals map).
• v = the light vector to the sun (unit length).
• · = the scalar product operator for two vectors.

### Effect of shadows, anisotropic ambient lighting

If the pixel has been shadowed, then the direct sunlight term (Sunrgb × N·v) is ignored and only the ambient light term (Ambrgb × (Nz + 1) ÷ 2) is considered.

Note that the Nz component in the ambient light term implies ambient lighting is anisotropic. Thus, steep terrain does not receive light from the whole sky hemisphere, and in the limiting case that the land is tilted at 90 degrees (thus Nz = 0) the terrain will see only half of the sky dome, in which case the ambient term becomes Ambrgb ÷ 2. This hack that assumes the sky dome is a uniformly bright Lambertian shell, which it most certainly is not, but the effect is still better than not doing it.

### Light absorption by water

If the heightfield pixel is under water, then both Sunrgb and Ambrgb are modified by Beers' / Lambert's law [link: Wolfram Research] to give physically accurate light maps for the seafloor / lake bottom. The equation goes a little something like this:

I / I0 = Txe-λx

where the ratio I / I0 is the intensity of the light at a depth of x as a fraction of the intensity that was incident on the surface, for a medium with an attenuation coefficient of &lambda;, and a surface transmission coefficient of Tx (discussed below). This equation is applied to each colour individually, as red, green and blue light are absorbed differently by water. In the 'Light/water effects' wizard you set the depths at which these colours are attenuated to 1/2 of the original transmitted light.

The relationship between half-depth (x1/2) and absorption coefficient (λ) is:

x1/2 = - loge(2) / λ

The transmission coefficient Tx is calculated using the Fresnel relations [link: Georgia State University]. Second-year university optics at its very best.

Anyhoo, I'm sure you get the idea.

The heightfield is this case was 'wrapping', so shadows were cast across map edges.

The basic approach of shadow casting is described by Mircea Marghidanu in this GameDev article.

The method described in that article involves a raycast from the sun to every pixel in the heightfield (or vice versa) to test if each pixel is shadowed. The L3DT routine is a little different insofar as it casts a 'shadow ray'1) from each heightfield pixel in the direction away from the sun, shadowing all heightfield pixels that lie beneath the ray. The effect is the same, but with less raycasts through empty space. The raycasting terminates when the ray intersects the terrain, which is immediate for any non-shadowing pixel (which is to say, almost all of them). Furthermore, we do not cast shadows from pixels that have been shadowed. This routine is rather fast because it minimises the number of ray-casts.

In L3DT, the 'shadow rays' in the casting routine are refracted when they enter water, in accordance with Snell's Law [link: Wolfram Research]. The effect is subtle, but I sleep better knowing it's there.

## High-resolution light mapping

This option is only available in L3DT Professional.

For pretty terrain rendering, it is common to use a 'high-resolution' terrain texture that has a finer pixel size than the terrain heightmap (see high-resolution textures). To get the best out of this practice, you need to also generate a high-resolution light map, so that features like shadows don't get washed out. This is exactly what L3DT Professional does:

The process is a simple extension of the lightmapping algorithm, whereby the heightfield is interpolated for the shadow-casting and the normals map is interpolated for the bilinear lighting. Additionally, the shadow edges are cleaned using a 3×3 anti-aliasing filter to remove pixellation. This filter only applies to the shadow state, and thus other lighting data retains its normal sharpness.

## Bump mapping

This option is only available in L3DT Professional.

Bump mapping, as described by Wikipedia, is a process by which the normals map of a surface is perturbed during light rendering, typically by an image, to produce more detailed and realistic surface relief. In L3DT, each material may have its own bump map(s) that are overlayed during the generation of the light map. Below you can see the texture and corresponding bump map for a rock land type:

The cliffs in the left image below shows the effect of bump mapping, where the apparent surface relief is enhanced when compared to the non-bump-mapped image (click for larger view):

Note that the effect of bump mapping is most noticeable when the light is at glancing angles to the surface. When the light direction is perpendicular to the surface, bump mapping has a lessened effect.

Here is another example, using bump mapping to apply the appearance of sand dunes:

With bump mapping Without bump mapping

There are even prettier bump mapping screenshots in the development blog.

1) Yes, I do understand that no such thing exists. It's simply a convenient geometric construct.