Fabien Sanglard's non-blog
Light scattering with openGL
November 25th, 2008
The actual stuff
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).
- Easy to setup.
- Gorgeous result.
- 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:
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.
- 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 ;
vec2 deltaTextCoord = vec2( gl_TexCoord.st - lightPositionOnScreen.xy );
vec2 textCoo = gl_TexCoord.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;
Add a comment
Neat example you have developed.
i used it successfully on multiple occasions but recently i migrated to a mac mini with intel hd graphics 4000 512 mb and the light scatter shader creates strange noise.
i see the same on a laptop with NVIDIA GeForce GT 650M 1024 MB, not thought on a mac mini with the radeon graphics card.
any ideas why?
I managed to fix that by adding
gl_FragColor = texture2D(firstPass, gl_TexCoord);
at the top of the shader.