# Atmospheric Effects & Volume Rendering

CS 481 Lecture, Dr. Lawlor

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).
Eventually, you can't see the difference between distant geometry and the sky, because all the geometry's light has been scattered out, and replaced with scattered-in sky light.

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-kt.   Then
d G / d t  = -ke-kt
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-kt.  The fraction of atmosphere light scattered into the ray is 1.0-G(t).  See the underwater example codes for how this is implemented.

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!

## Bounded Scattering Media

The simplest non-infinite scattering media is bounded within an object.  There's an easy way to compute how much scattering happens, which is to compute the t difference between the ray entry and ray exit points.  This "span" can then be plugged into the scattering equation above, so the amount of light scattered out is exp(-k*span).

## Non-Uniform Scattering Media

For non-uniform scattering media, we cannot analytically solve a differential equation to figure out how much light scatters in or out, because the scattering rate changes--the constant k is not a constant!  Occasionally the scattering rate will change at only a few discrete points, so you can analytically solve between the points; or you can find a good analytic approximation for the real scattering rate; but more often the scattering rate varies arbitrarily, for example according to a 3D volume lookup table.

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) {		color = scattering_function(C+t*D,old color);	}`
This is amazingly flexible, because:
• 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.
Commercial volume renderers almost always use some sort of finite step size like this.