Fabien Sanglard's non-blog
Light scattering with openGL
Joy with openGL GLSL.
The actual stuff
Overview
This article is based on [Mitchell] publication. It describes and provided an openGL implementation of the technique to perform realtime light scattering (god's ray).
Pros:
- Easy to setup.
- Gorgeous result.
Cons:
- Consum a lot of fragment horse power, especially with a high # of samples.
- Cannot re-use the precomputation to cast Shadows (like in [Mitchell2]).
High level description
The best description is by Kenny Mitchell in GPU Gems 3 but while you wait for your Amazon delivery, here is a little bit more informations: It's a 2D post-processing effect including three stages :
- Render offscreen with a FBO: the light source and the occluding objects, no shaders involved here. In order to save cycles, you can render to a lower resolution (factor 2 gives good results) and disable texturing/depth testing.
- Clean the depth buffer, render the scene normally to the framebuffer.
- Switch to Orthogonal projection and blend the FBO with the framebuffer, activating the shader in order to generate the "God's ray" effect .
High resolution screenshots
Low level description
The keystone of the process is the shader which compute the final color by taking sample along the segment [current fragment - light position].
One way to do this is to calculate light position in screenspace on the CPU side and pass these value as an uniform variable. Two ways to do this:
- You can use the gluProject method.
- "Manually", with the following code:
The fragment shader
The keystone as aforementionned, notice the NUM_SAMPLES const, the more sample taken, the less aliasing...and the more load on the GPU shaders units. A lot of variables can be tuned to adjust the effect:
- Number of samples: The more the merrier.
- Decay
- Density
- Weight
- Exposure
- Occluding object FBO rendering resolution (firstPass texture resolution)
uniform float exposure;
uniform float decay;
uniform float density;
uniform float weight;
uniform vec2 lightPositionOnScreen;
uniform sampler2D firstPass;
const int NUM_SAMPLES = 100 ;
void main()
{
vec2 deltaTextCoord = vec2( gl_TexCoord[0].st - lightPositionOnScreen.xy );
vec2 textCoo = gl_TexCoord[0].st;
deltaTextCoord *= 1.0 / float(NUM_SAMPLES) * density;
float illuminationDecay = 1.0;
for(int i=0; i < NUM_SAMPLES ; i++)
{
textCoo -= deltaTextCoord;
vec4 sample = texture2D(firstPass, textCoo );
sample *= illuminationDecay * weight;
gl_FragColor += sample;
illuminationDecay *= decay;
}
gl_FragColor *= exposure;
}
References
[Mitchell]: Kenny Mitchell "Volumetric Light Scattering as a Post-Process" GPU Gems 3 (2005).
[Mitchell2]: Jason Mitchell "Light Shaft Rendering" ShadersX3 (2004).


