Rendering realistic and dynamic skies is a challenging problem in real-time computer graphics. The math behind light scattering is too complex to solve directly in real-time, but it can be done offline.
This program solves the atmospheric scattering equations for every possible view and sun direction, and stores the results in look-up tables. The tables can then be sampled in a fragment shader to reconstruct an accurate rendering of the sky at any time of day.
This branch contains the code that precomputes the scattering tables. For an example of using the precomputed data in a renderer, please look at this repository's gh-pages
branch.
Before light reaches your eye, there is a probability a portion of it will be absorbed by aerosols (Mie theory) or scattered into a different direction by molecules (Rayleigh theory).
The amount of light reaching your eye after undergoing either Rayleigh or Mie scattering within the atmosphere is described by the following equation:
For a complete explanation of the math behind atmospheric scattering, I recommend Gustav Bodare and Edvard Sandberg's excellent thesis on atmospheric scattering.
This program precomputes the above integral for:
To use it:
cd
into atmosphere/
make
../build/atmosphere
.You can specify a number of runtime flags to control the pre-computation:
-o <output_dir>
puts the results into the specified directory. (The directory must exist).-n
normalizes the pre-computed results into the [0, 1] range. This helps preserve precision in the double to float conversion process.-exr
outputs to .exr
files rather than binary float arrays. (This requires the OpenEXR library. You might have to set the path correctly in the makefile).This program outputs three files:
rayleigh
contains the precomputed Rayleigh scattering table as either a binary-encoded float array (.bin
) or an HDR image (.exr
).mie
contains the precomputed Mie scattering table as either a binary-encoded float array (.bin
) or an HDR image (.exr
).results.txt
contains the necessary constants to correctly render the atmosphere in your renderer of choice.I excluded the spectral intensity Ii(λ)
and phase function F(θ)
terms from the pre-computed results. They are constant anyway, and in deferring them to a fragment shader we can avoid some visual artifacts around the sun.
I also provide the option to normalize the table values in the [0, 1] range to preserve as much precision as possible when converting from doubles to floats. After you sample the tables in a fragment shader, you must un-normalize the values with the following formula: val = val * (max - min) + min
. The max and min values are written into results.txt
as part of the pre-computation process.
For a complete example of using the precomputed data in a renderer, please see this repository's gh-pages
branch. The basic process is outlined below:
u = 0.5 * (1.0 + sign(cosViewZenith)*pow(abs(cosViewZenith), 1.0/3.0));
v = 0.5 * (1.0 + sign(cosSunZenith)*pow(abs(cosSunZenith), 1.0/3.0));
(u, v)
.min
and max
constants from results.txt
.results.txt
to compute the radiance.results.txt
to compute the rgb color.rgb = pow(1.0 - exp(-rgb), 1.0/2.2)
.