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.