Github

I really like clouds. That's why I built a real-time volumetric clouds renderer with my CS1230: Computer Graphics teammates, based on this talk at SIGGRAPH 2015 from the development team of Horizon Zero Dawn. The renderer we developed with OpenGL and C++ features intricate cloud shapes generation, realistic lighting effects, and a physically-based sky that varies throughout the day.

I. Shape

Worley noise is a type of cellular noise where the value of each point in space is determined by its distance to the closest neighbor in a set of random feature points. It is great for modeling the density of clouds because when inverted (hover or tap on the image below to see it yourself!), it produces those nice bobbly patterns that resemble clouds.

Generating Worley Noise, Fast

The first optimization we make of the above process is to take stratified samples of feature points in regular grid cells (let's call them grid points) instead of uniformly in space. This reduces the quadratic number of candidate neighbors dramatically to 27 (constant!) in 3D since we can easily find the cell any point belongs. Here is an illustration for the 2D case as it is slightly easier to draw.

For even greater efficiency, we write a compute shader and feed all the grid points to it. The compute shader can then calculate a 3D noise map with hundreds of GPU cores in parallel. Long live parallelism!

Fractal Noise

To create sufficient details for our clouds, we generate two sets of 3D Worley noise texture: a high resolution (200x200x200) one that controls the overall shape of the clouds, and a second lower resolution (64x64x64) one that adds details to the base shape by eroding lower-density regions of the shape texture. Each texture map has 4 channels of 3-layer Worley noise of increasing frequency. This overlaying process generates fractal-like noise textures that add great details to our clouds. Below are slices of the four channels of our high resolution shape texture.

Worley noise channel R Worley noise channel G Worley noise channel B Worley noise channel A

Making it Tileable

One last pitfall of our previous scheme is that textures created that way are not tileable: visible seams appear when we place two copies of a texture next to each other. Luckily, there's a simple trick to fix that: wrap the grid points around the unit cube. This makes the grid points periodic and thus makes the noise texture tileable as well.

II. Lighting

We implemented the following two components for fast, realistic lighting for clouds.

Beer's Law

Beer's Law models the exponential decay of light energy as it propagates in an attenuating medium, like clouds. In a uniform medium, the energy of light attenutaes in a simple exponential function of the distance travelled.

For light travelling through a heterogeneous medium, we can modify the equation above slightly to acount for the varying medium density.

This type of attenuation can be found at multiple spots in the volume rendering equation. Diagram showing different terms in the volume rendering equation.

Implementing the equation in code that runs in real time, however, means that we have to make several approximations. Since a closed-form solution is clearly not feasible, we need to resort to Monte-Carlo integration instead. That means taking mutiple samples along a ray and doing discrete sums of light energy and transmittance.

The obvious headache now is the in-scattering term C. (We can ignore emssion since clouds don't shine themselves.) Evaulating it precisely involves taking an integral of all in-scattered light over all possible direction - but each beam of in-scattered light obeys the exact same formula we are trying to solve! Instead of fighting this recursive nightmare, we make the assumption that most of the in-scattered energy comes from the primary scattering path: Sun → sample point → eye. This simplifies the in-scattering term dramatically, down to just one integral

This is now much more computationally tractable and it gives us a base lighting model to build upon.

Clouds rendered with Beer's Law.

Anistropic Scattering

Anistropic scattering may sound like a scary term. But it really just means that light prefers to scatter in some direction rather than others. In clouds, light tends to scatter forward instead of backward. This is what gives clouds the silver lining look when the Sun shines right through them.

Following the SIGGRAPH talk, we use the Henyey-Greenstein phase function, originally proposed in 1949 for modeling diffusion of light in galaxies, to produce the anisotropy in cloud lighting.

Adding the Henyey-Greenstein phase function to Beer's Law gives us the following nice result.

Clouds render with Beer's law and Henyey-Greenstein phase function.

Notice how much brighter the cloud has become near the sun. We just got our silver lining!

III. Sky

Coming soon...