Search Unity

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.

atmospherics_02

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[1]. 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

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.

scatter_Rayleigh
Rayleigh contribution in different scattering configurations.

Mie Scattering

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.

scatter_Mie
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.

Height Scattering

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.

scatter_Height
Height contribution in different scattering configurations.

Scatter Occlusion

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.

scatter_Occlusion
Unbiased occlusion in different scattering configurations.

Putting it all together

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.

scatter_Scattering
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.

scatter_OccludedScattering
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.

scatter_None
Final composition.

Playtime!

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.

References:
[1]: Display of The Earth Taking into account Atmospheric Scattering
http://nishitalab.org/user/nis/cdrom/sig93_nis.pdf

[1]: Display Method of the Sky Color Taking into Account Multiple Scattering
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.75.5595&rep=rep1&type=pdf

[1]: Display of Clouds Taking into Account Multiple Anisotropic Scattering and Sky Light
http://www.researchgate.net/publication/220720838_Display_of_Clouds_Taking_into_Account_Multiple_Anisotropic_Scattering_and_Sky_Light

The HDR sky in the package is from NoEmotionHDRs (Peter Sanitra) / CC BY-ND 4.0. Used without modification.

10 Comments

Subscribe to comments

Comments are closed.

  1. Rubén García

    July 17, 2015 at 1:32 pm

    Have you noticed that using Unique Shadows ( Blacksmith asset ) disables Atmospheric Scattering Occlusion while entering play mode? (using Atmospheric Scattering in Deffered mode, with the post process script attached to the camera ). Is this a known bug?

    1. Rubén García

      July 17, 2015 at 1:42 pm

      I have noticed that this bug only occurs if the “Use Scene Capture” of unitque shadow script is active.

  2. hi

    unity 5 has two deferred rendering path mode: deferred(not sure this is traditional) and legacy deferred(light prepass),
    i found that atmospheric effect can work on legacy deferred and forward but switch to deferred
    it doesn’t work.

    1. hi sorry
      i know how to do it in deferred. just add deferred script to camera

      thanks

  3. I imported the Blacksmith scene and I see this huge object called big_bad_sphere. When I turn it off and on the skybox changes, and I can’t move outside of it in the editor.

    It seems to affect atmospheric scattering somehow. Can you explain this object in detail as well?

    1. Torbjorn Laedre

      June 30, 2015 at 11:56 pm

      Hi Glenn!

      If you get the atmospherics example project linked in this post, this and quite a bit more is explained in the included readme file.

  4. Rodrigo Medeiros

    June 26, 2015 at 7:26 pm

    Hello friends!!!

    I have problems related to the Atmospheric Scattering. Added the asset in a clean design, but when I put the package on it, the image is all pink, it simply does not render and does not show any error!

    I’m using version 5.1.1, the my PC settings are:
    Intel Core i7, 8GB RAM, 1GB Nvidia 630m.

    1. Torbjorn Laedre

      June 27, 2015 at 10:51 pm

      Hi Rodrigo!

      Unfortunately, these releases uncovered an issue affecting some users. There’s a fix in the Unity 5.1.1p2 patch release which is available now.

      To fix the ‘everything pink’ issue in some of the Blacksmith packages, please install this patch release and then reimport the failing shaders or the entire project.

  5. Hello, Unity!
    Is this Atmospheric Scattering solution works only with forward rendering path?

    1. Torbjorn Laedre

      June 24, 2015 at 5:28 pm

      No, it also works in deferred by applying scattering to the opaque parts in a custom image effect. (the translucent objects and the sky are always rendered as forward)