Light interacts with large masses of air in one of two ways:

- The distant geometry's light is scattered out of the ray by the air, causing distant geometry to get dimmer.

- Sunlight is scattered into the ray by the air, causing distant geometry to get bluer (more sky colored).

Numerically, the fraction of geometry light remaining after travelling a distance t, which we'll call G(t), starts off with G(0)=1.0, and drops off at some rate until G(infinity)=0.0. We have to assume something about the rate at which the atmosphere scatters light. For example, if we assume the background geometry's light gets darker by 10% for every unit distance, or:

d G / d t = -0.1

Then we can integrate to find G(t)=1.0-0.1*t. The only problem with this is that G(10)=0.0, and then G goes negative!

A better choice is to assume that the sky scatters out 10% of the *existing* light per unit distance, or:

d G / d t = -0.1*G(t)

The solution to this is a function that is its own derivative, G(t) = e

d G / d t = -ke

so we can read off that the scattering rate k=0.1 light fraction per unit distance.

In a raytracer, if a ray travels a distance t, then we need to darken the geometry at the end of the ray by G(t) = e

Typical scattering rates k for the real atmosphere are a few percent per mile for clear air; in foggy conditions this can reach several percent per foot!

In this general case, the standard solution is to pick some finite dt ray step size, for example 0.01 units, and step along the ray keeping track of how the light changes. Typically, this looks something like:

for (float t=entry_t; t<exit_t; t+=dt) {This is amazingly flexible, because:

color = scattering_function(C+t*D,old color);

}

- The scattering rate can change according to any method you'd like: via a function, via a repeating table, via a random number generator, or any combination. Weird artifacts will be visible if the scattering rate changes faster than the step size, but smooth functions work well.
- You can even change the step size dt as you step along, taking
large steps in smooth areas and small steps when passing through
detailed objects. This can perform far better than a fixed small
step size.

- How the light changes at each step can be arbitrarily complex. Most modern volume renderers actually evaluate a full diffuse+specular lighting function at each step, using the volume gradient to approximate the "surface" normal (there is no actual surface). Some volume renderers actually shoot a shadow ray for each step, which gives beautiful shafts of light, although this takes a really large number of shadow rays.
- You could even change the ray direction at each step, for example
to follow a nonlinear ray path through a nonuniform material or near a
black hole.