Early in the planning phase of The Blacksmith, we knew we wanted an atmospheric scattering solution that would give us a little bit more detail and control than the built-in fog options. In particular, we wanted to emphasize the aerial perspective effect in some of the more expansive shots in the movie.
As we started working towards a scattering solution for the project, we initially implemented and played around with the simulation models presented in several papers from Tomoyuki Nishita. After some experimentation and prototyping of different shots, we eventually decided that we would be better off aiming for a model that allowed extensive artistic control for each of the shots in the short film. We wanted a solution that would allow us to get close to the primary elements of the physical models, but that also allowed us to break any and all rules when required. We also needed the solution to not have a huge impact on the runtime performance of the short film, and set aim to be able to do most of the calculations per-vertex as opposed to per-pixel.
We set a goal of trying to emulate the combined effects of Rayleigh and Mie scattering from the physical models. We also added a third element representing various types of low-altitude scattering effects; collectively named height scattering. Another key divergence from the physics based models was that we decided to keep using HDR sky textures, as opposed to procedurally generating the sky and clouds. The obvious downside to this is that setting up something like dynamic time of day (which we didn’t need for The Blacksmith) becomes a bit more complicated, whereas the primary advantage is retaining full artistic control over the sky.
Rayleigh scattering of sunlight in the atmosphere is the reason for the bright blue hue of the daytime sky, and the reddening of the sun and horizon at sunrise and sunset.
In our emulation, we omit the sun itself, and focus just on modelling the colors and extinction produced by the sunlight’s in- and out-scattering. A visual representation of the sun can be added either in the sky texture, as part of Mie scattering, as a sun flare sprite, or any combination of these. At its simplest core, the density of our rayleigh scattering boils down to a glorified exponential function modulated by the Rayleigh phase function. However, we have some additional control over the data that gets put into it, and the data we extract out from it. Since we don’t model light of different wavelengths travelling through the atmosphere, the densities we calculate are scalar values. We use an HDR color ramp to allow for different hues of in-scattered light at horizon and towards zenith, and use a distance aware function for composing the final hue.
Rayleigh contribution in different scattering configurations.
Mie scattering of sunlight in the atmosphere contributes to the bright halo around the sun, the grey-white appearance of clouds, and the haze that can be seen over polluted cities. As opposed to Rayleigh, which scatters light in an almost uniform shape, Mie scattering is strongly forward directional.
In our emulation, we let Mie scattering primarily represent the haze and halo around the sun. As such, we almost always tint it to compensate for the fact that our Rayleigh emulation ignores the sun. Technically, our Rayleigh and Mie functions are very similar, with the significant difference being the phase function that is applied to the output. Like many other implementations, we use the Henyey-Greenstein scattering function for controlling the anisotropy – or forward directionality – of the Mie scattering.
Mie contribution in different scattering configurations.
People who have read the research papers might scoff at our choice of names, given that we take certain ‘liberties’ in what we include in each of the emulations. We found early on that people generally used the name Rayleigh when describing ‘sky scattering’ and Mie when describing ‘sun haze’, so we decided to just keep rolling with those names even after the implementation models were simplified from the physical models.
The height scattering element represents a mish-mash of various low-altitude scattering effects, including radiation fog, ground haze, and low-lying clouds.
Our implementation of height scattering is fairly straightforward; height density is calculated from a defined sea level and height falloff. This then scales the distance-based exponential density, and the whole thing is tinted to the desired color.
Height contribution in different scattering configurations.
Since our scattering contribution is primarily caused by sunlight scattering towards the observer, away from the observer, or being absorbed by particles on its way to the observer, it makes sense that something should be happening if objects are blocking the sun’s light.
To handle such cases, we ray-march through the directional light’s cascaded shadow map and accumulate the amount of occlusion along the ray in a downscaled, off-screen buffer. When applying the scattering to the output pixel, we upsample this occlusion map with an edge-aware filter, and use it in composing the final color for the pixel. This combining stage is where we get into a little bit of trouble; since our solution is single-scattering only, we can’t just go masking out all in-scattered light, as that would leave us with a very dark and unnatural image. We also didn’t want to expand the solution to handle the more complex and expensive multiple-scattering. In the end, the solution for us was to invent an ‘indirect factor’ where you could just explicitly designate a certain percentage of scattering to be treated as it were indirect instead of direct.
Unbiased occlusion in different scattering configurations.
All that remains now, is to combine the different elements to compose the final image. Adding together the Rayleigh, Mie and Height elements gets us started with a nice composition of the different scattering colors.
Combined Rayleigh, Mie and Height scattering.
Next, we need to make sure we put that occlusion buffer to good use. We use different strength parameters for tweaking the amount of occlusion applied to direct, indirect, cloud and sky scattering.
Combined and occluded scattering.
Finally, the only thing that remains is to mix the scattering with the rendered image. We darken the transmitted image by the total accumulated extinction, and lighten it by the total accumulated in-scattering. This yields the final composition for our example scenes.
We’ve extracted the atmospheric scattering to a separate project which you can get from the Asset Store. In addition to all the code and shaders making up the solution, the project also contains all configuration presets used to generate the images in this post. Don’t forget to check the included readme for details about what the different configuration options mean.
: Display of The Earth Taking into account Atmospheric Scattering
: Display Method of the Sky Color Taking into Account Multiple Scattering
: Display of Clouds Taking into Account Multiple Anisotropic Scattering and Sky Light