Fullscreen Post Processing: Explosion Shockwaves
A really long time after having conceived the idea for a gun effect doing the mighty Fusion gun justice after having seen Half-Life 2's Strider main gun effect, I have finally come around to implementing it. Lots of things had happened in the D2X-XL since I had first wished to see such an effect in Descent, and all provisions (namely rendering to texture using frame buffer objects) have been made quite a while ago.
I admit that I was a bit too lazy to write the distortion shader from scratch, so I googled for one and found it on Geeks3D. The shader there wasn't more than a good start though:
- since it used relative coordinates, its distortion effect was more or less elliptical
(depending on the render window's width:height ratio) - the effect it created always went across the entire screen
- it could only handle a single effect
- it wasn't coded too efficiently
My final shader handles all my requirements quite gracefully, creating up to eight nice, local, spherical effects. For the sake of simplicity, effect parameters are passed via GLSL's gl_LightSource object.
uniform sampler2D sceneTex; // texture with current frame
uniform int nBias; // implode/explode
uniform int nShockwaves; // number of effects
uniform vec2 screenSize; // render window width and height
uniform vec3 effectStrength; // 10.0, 0.8, 0.1 * render window width
void main() {
vec2 tcSrc = gl_TexCoord [0].xy * screenSize; // create absolute texture coordinates
vec2 tcDest = tcSrc;
int i;
for (i = 0; i < 8; i++) if (i < nShockwaves) {
vec2 v = tcSrc - gl_LightSource [i].position.xy; // compute direction from effect center to pixel
float r = length (v); // compute distance of pixel to effect center
float d = r - gl_LightSource [i].position.z; // compute distance from pixel to effect area
if (abs (d) <= effectStrength.z) {// are we in the area of the wave ripple?
d /= screenSize.x;// scale back
d *= (1.0 - pow (abs (d) * effectStrength.x, effectStrength.y)); // create the wave ripple
d *= pow (gl_LightSource [i].quadraticAttenuation, 0.25); // dampen wave at end of effects lifetime
tcDest -= v * (nBias * d / r) * screenSize; // compute final pixel coordinate and sum it up
}
}
gl_FragColor = texture2D (sceneTex, tcDest / screenSize);
}
Here is a video showing the effect:
The effect shown in the video is not 100% physically correct - it doesn't take depth information into account. The current shader does however to avoid shockwave effects distorting geometry or objects in front of them.