Submitted by , posted on 25 June 2002
Image Description, by
Lots of novel things happening here...
First of all, you can use the mouse to rotate the window on top of your
desktop. The simulator responds to this by simply rotating the gravity vector.
These forces have been used to form a breaking wave in the image above.
Secondly, the literature on which the simulation is based distributes marker
particles throughout the liquid area, but I developed a way to track the
liquid/air interface requiring particles only near the surface. These
particles are reseeded every frame based on dynamic rules which also help
converse mass better. (Although, since the computational mesh is anisotropic,
screwing with gravity adversely affects this.)
I also came up with a novel technique to render the liquid. I'll describe it
here since the idea is applicable to any 2D or 3D particle system. (The demo
only distributes particles around the liquid surface but assume here that the
liquid is entirely a 2D particle system.) The approach is a hardware
implementation of the classic 2D metaballs effect, which basically amounts to
thresholding a summation of 2D density functions...
First I procedurally generate a Gaussian density function in a 16x16 alpha
texture-map and set up a texture which can be rendered to, which I'll refer to
as the p-buffer. Now, for each frame, I first clear the p-buffer to the colour
(0,0,0,0), set the vertex colour to (0,0,0,1) and enable blending with ONE/ONE
as the src/dest functions. Then I render each particle into the p-buffer as a
16x16 pixel quad, texture-mapped with the density function. After this, I set
the vertex colour to (1,1,1,0) and render another quad which covers the entire
p-buffer. This can be mapped with a texture of your choice. We've just
completed the summation of the density functions for each metaball. To perform
the thresholding all we need do is enable alpha-testing, set the function to
'greater than whatever threshold you choose' and render a quad to your
frame-buffer, texture-mapped with the p-buffer.
You'll notice in the demo that the liquid surface is also outlined. To achieve
this, we start by filling the alpha channel of the p-buffer exactly as above.
From here we render a quad mapped with an "ink" texture to the p-buffer, and
render a quad mapped with the p-buffer to the frame-buffer, same as above. To
create the effect of an outline we now render a second pass (using our
original map) opaquely on top of the ink pass, but using a slightly smaller
threshold so that a "band of ink" from the first pass remains visible. This
means we need to write a new texture-map into the RGB channels of the
p-buffer, making sure not to modify the alpha channel. To do this, we could
just mask off writes to the alpha channel but this was incredibly slow on my
GeForce. A better solution is to first zero the RGB channels of the p-buffer
by setting the src/dest blend functions to ZERO/SRC_COLOUR, the vertex colour
to (0,0,0,1) and rendering a non-texture-mapped quad which covers the
p-buffer. Now we can set the blend functions back to ONE/ONE and the vertex
colour back to (1,1,1,0) and proceed to render the second pass exactly as
before but remembering to use a smaller threshold.
This same method can also be used to render level set representations of 2D
liquid or fire by simply copying the distance grid to an alpha texture of
equal dimensions each frame and letting bilinear filtering interpolate the
distances. In the case of fire, multiple passes, each with a different
alpha-testing threshold, can be used to render different parts of the flame
with different texture-maps. Alternatively, a spectrum could be defined in a
1D texture-map which is looked-up using the interpolated distance values.
The "looking through liquid" effect is nothing more than a preprocessed
512x512 capture of the desktop. The procedure I came up with involves applying
two wave deformations, blending in a a bluish tint with Gaussian noise and
applying two Gaussian filters. Of course, in the real world refraction doesn't
occur when looking through a tank of water like this but I thought the
waviness looked nice.
You can download the demo here:
It has been tweaked and tested only on my PIII-700 running Windows 2000 with a
GeForce 1 and I'm using OpenGL extensions which I think require at least 28.32
drivers. There will be a small delay when you run the program to generate the
textures. Performance is fill-rate bound on my configuration so by default the
demo takes three Euler steps (the time-step is fixed for stability reasons)
per frame giving me 35 FPS. You can specify this ratio yourself with the
number keys however and I suggest you try this. You can also toggle the ink
pass by pressing 'i'. To quit either press escape or change focus from the
window. Finally, you may notice that sometimes little "explosions" occur when
the water velocity gets too high and the simulator becomes unstable, so be
gentle. Also note that I took out the anti-aliased window edges and reflective
glass pane feature (shown above) to accomodate the unconventional window
styles of WinXP.
If for some reason you can't run the demo, you can download a 512x512 DivX
movie of the above executable running on my computer here: