http://www.opengl.org/resources/code/rendering/mjktips/caustics/ OpenGL-rendering of Underwater Caustics

OpenGL-rendering of Underwater Caustics

Underwater environments look quite different from above-water environments. Think about swimming underwater in a swimming pool. Light hitting the rippling surface of the water creates focusing effects or "caustics" on underwater surfaces (such as the walls of a pool). This interplay of light underwater is an important cue for distinguishing underwater environments from above-water environments.

Think about a 3D point-of-view game where the player can descend into watery areas. Wouldn't it be great to add caustic effects to underwater surfaces? It turns out such effects are straightforward to generate using a second blended rendering pass in conjunction with OpenGL's texture coordinate generation functionality and some caustic texture patterns developed by Jos Stam. Here's an example of underwater caustics (ie, rippling effects) simulated with OpenGL rendering:

What you miss in the static pictures above is that the caustic patterns are actually dynamic so that every frame you see a slight shifting of the caustic pattern. You'll need to run the program on an OpenGL implementation with good texture mapping and blending support to get the real jist of the demo. An SGI O2 works great.

Jos Stam has several web pages with his various research results dealing with periodic caustic maps. Check out Periodic Caustic Maps, Random Caustics: Wave Theory and Natural Textures Revisited, a SIGGRAPH '96 technical sketch by the same name, and Aperiodic Texture Mapping. What Jos Stam has done is created a technique for generating caustic-looking textures that are both repeated spatially and temporally.

The rendering technique demonstrated on this page was first shown to me by Angus Dorbie. The credit for developing this rendering technique completely belongs to Jos Stam and Angus.

Here are some more snaphosts. In this next snasphot, note that the caustics appear on a cube just as well as they appeared on the sphere in the above snapshots. The color of the light has been switched to a pure white light instead of the greenish (watery) light in the above image. Note how the light color interacts with the caustics.

In the snapshot below, a positional light (instead of a directional light is used). Also note that the caustic ripples have been increased in size (simply by scaling the texture coordinate generation plane equations):

The two snapshots below show how the caustics interact with a crude dinosaur model. The image on the left has the caustics enabled. The image on the right is missing the second rendering pass to add the caustic ripples. This should help you see the contribution of the second pass:

Again, the contribution of the caustics is much clearer when animating.

The source code for the program pictured above is three C files and two header files: underwater.c, texload.c, dino.c, texload.h, and dino.h. The thirty-two 256x256 8-bit caustic texture image files used by underwater are contained in the following GNU-zipped Unix tar file: caustics.tar.gz. You will also need floor.rgb.

As mentioned earlier, the technique involves a second rendering pass over all objects in scene. The blend function used is:

  glEnable(GL_BLEND);
  glBlendFunc(GL_ZERO, GL_SRC_COLOR);

The important step is enabling texture coordinate generation with glTexGen:

  GLfloat sPlane[4] = { 0.05, 0.03, 0.0, 0.0 };
  GLfloat tPlane[4] = { 0.0, 0.03, 0.05, 0.0 };

  glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
  glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
  glTexGenfv(GL_S, GL_OBJECT_PLANE, sPlane);
  glTexGenfv(GL_T, GL_OBJECT_PLANE, tPlane);
  glEnable(GL_TEXTURE_GEN_S);
  glEnable(GL_TEXTURE_GEN_T);
  glEnable(GL_TEXTURE_2D);

The texture generation will map the currently bound caustic texture object onto every object in the scene based on its object coordinates. See the underwater.c source code for more details.

As the briefest aside, I'd like to point out that many other 3D APIs (Direct3D in particular though) lack the texture coordinate generation capability (present in OpenGL since release 1.0) that makes possible the object-coordinate texturing of the caustic patterns onto arbitary geometry.

- Mark Kilgard (mjk@nvidia.com)