A quick technique for light scattering clouds

Hi all,

Here is an interesting demo that I wrote with my friend Andres Reinot last week. We came up with a fast way to draw something that approximates light scattering in clouds.

Code and build systems for mac, linux and win32 are provided.

The approach is quite simple - draw a set of screen aligned sprites, each of which have diffuse, normal, and translucency maps. Then a fragment program is run to compute the lighting on each sprite. The lighting equation consists of two terms - the diffuse which is computed in the standard method with a dot product between the normal and light vector, and the translucency which is computed as the square of the dot product of the eye and negative light vectors, modulated by the translucency map. Basically what that means is that when the light is behind the sprite, you will see light shine through toward the viewer in the pattern set down in the translucency map. “Wrap” lighting is also used in the translucency dot product to ensure that the translucency term is also partially present when the light is coplanar with or in front of the sprite.

I’d appreciate any feedback or suggestions you may have on the technique. More info and screenshots are available here: http://www.vrac.iastate.edu/~areinot/clouds/clouds.html

Jeff Russell

I see some popping effects when I rotate. Is this a sprite related problem?

Neat demo. But your makefile doesn’t build out of the box on Panther. Try something like

ifeq ($(HOSTTYPE),)
HOSTTYPE = $(shell uname -p)
ifeq ($(HOSTTYPE),powerpc)
COMPILER = g++ -Os -mdynamic-no-pic -I. -faltivec

The still image looks good, but in the animation the popping is quite annoying. Do you think you can do something about it?

The popping occurs because the sprites are depth sorted. Anyone have suggestions on how to fix it? Maybe fade the sprites out if they are close to intersecting one another?

You could use Depth Sprites. If you are unfamiliar with the technique, the idea is to replace depth per-fragment based on a pre-computed depth map, which can encode depth in one of the channels or 2 of the channels for 16-bit depth.

BTW, very neat technique. Glad to see people are looking for a solution to quick light scattering with such open minds. Nice work. I have a question though. Couldn’t you just store the translucency information in the alpha channel of the diffuse map (or am I missing something)?

If your sprites are sorted from high to low distances to the view point, try simply turning off depth writes before rendering them. It’ll still not be entirely ‘correct’, but it should get rid of the popping. This of course implies that you render the cloud sprites last, after all other objects in a scene.

The depth sprite approach should fix the popping issue at least partially - we’ll have to give that a try sometime. Disabling writes to the depth buffer does not fix the popping problem - since the sprites are depth sorted anyway and the popping occurs when one moves in front of another w.r.t. the viewer.

The reason that translucency is separate from alpha is twofold. Frstly a translucency map allows for colored masks on the translucency term. And second the translucency mask represents a different effect from the alpha mask - there are situations where a texel should be opaque (alpha = 1.0), but there should be some light scattering at that point (translucency = 0.5), hence the different maps.

How are you sorting them, using world space z values?
Computing distance from eye is more accurate.

They are sorted according to distance from the eye. I tried the z-distance approach first but found it to be much worse than the current setup.


i’ve the same problem in my cloud renderer. But since the target hardware doesnt support ARB_fp depth manipulation tricks as for depth sprites are no option. Any other ideas?

thanks alot.

perhaps u can do what i do with my translucent stuff (that is not sorted)

// only fragments with alpha’s greater than 0.5 will be written
float depth_val = clamp( 0.5 - diffuse_color.w, 0.0,1.0 );
gl_FragDepth = gl_FragCoord.z + depth_val;

ie only some of the pixls have there depths written at the correct place, here im using the alpha component as a test but maybe intensity or something else is better in your case