RainSaver is a screensaver for Windows that I made, which warps the desktop as if it were a liquid with raindrops hitting the surface. Here’s a little bit more in-depth information about how the tech behind that works. I used OpenGL for this project, so everything will be from that perspective, but I imagine the same techniques could also be applied in a Direct3D approach.
The first step is to grab an image of the screen to load into video memory as an OpenGL texture, which I won’t go into a whole lot of detail on, except to say that it involves a handy function called BitBlt. Next, I generate a “drop” texture. The drop texture is basically a normal map, where different colors indicate different normals. These colors are later used in the fragment shader to decide how much, and in which direction, to warp the final image. To actually generate it, I set the colors based on sine waves of the coordinates of the pixel relative to the center of the texture, and the alpha based on a sine wave of the distance from the center, setting negative alpha values to 0. It ends up looking a little like this:
// for each pixel in the 256x256 buffer dist = sqrt((float)((y-128)*(y-128) + (x-128)*(x-128))); dropBuffer[y][x] = 128+(x-128)*(sin(dist/2.0f)); dropBuffer[y][x] = 128+(y-128)*(sin(dist/2.0f)); dropBuffer[y][x] = 255; // remove inner ring(s) if (dist < 90) dropBuffer[y][x] = 0; else dropBuffer[y][x] = pow(MAX(0.0f, sin(dist/8.0f)), 12)*255.0f;
There’s also a wavy texture that is generated from cosine and sine waves of x and y coordinates, respectively, and smoothed noise for alpha values.
New raindrops are randomly created every so often, and existing ones expand over time. All existing drops are drawn into a frame buffer object (FBO), along with the wavy texture over the whole screen. Finally, a full screen quad is drawn with multitexturing, using the desktop texture and the FBO distortion texture, and the fragment shader takes it from there, using the final colors in the distortion texture as normals to determine distortion and shading. The GLSL for the warping alters the texture coordinates, like so:
vec2 coords = gl_TexCoord.st; coords.s = coords.s + (change.r*2.0 - 1.0)/40.0; coords.t = coords.t + (change.g*2.0 - 1.0)/40.0;