Water; it's all around us. Out of a tap, out of the sky - we'd be dead without it. Some may argue that Brits get an unfair slice of the rain pie, and I'd agree there, but without water there would not be life.
But enough of my ramblings. Water is incredibly hard to represent accurately in games due to its liquid nature and complex physics (indeed, people still don't fully understand the physics of oceans, having to turn to quantum physics to solve a particular problem involving 'freak waves'), but all the same, it is an integral part of many games. I feel that their implementations of water are mostly horribly hacky (Half-Life with its sin(t) 'waves', UT2K3 with its bowls of jelly) and could do with a boost - so, I'm going to explain an algorithm for static water simulation that's been around for a fair amount of time, but hasn't been paid much attention.
This isn't the best algorithm in the book by far - it has a certain caveat which I'll explain later - but other, better ones involve Fourier transforms, and thus a heck of a lot of computational power which we simply can't afford to spend. At least, not until we're into 20 GHz computers, anyway.
For this algorithm to work, the water needs to be represented in a grid (size n
) with each element of the grid being a point on the surface of the water. It also computes the next time step using the last two time steps, and so at least two sets of the grid need to be stored. There are also various constants:
- d - the distance between adjacent vertices in the grid in both the x and y directions
- t - the time interval between calculations of the grid - higher for a faster simulation, lower for slower
- mu (greek letter - a u with an elongated left side) - the viscosity of the fluid - a smaller value allows ripples to exist on the surface for a long time, a larger value makes them diminish faster
- c - the speed at which ripples travel across the surface
So, how do we use all these values? Well, there's one large equation which gives the wave height at grid position i, j
and time step k
(so that the actual position of the vertex being modified is di, dj
and the actual time value is kt
), which is this: (Actually, it gives the height at the next time step, not the current one, but it's the same thing.)
I know exactly what you're thinking. You're thinking "If I calculate that for every vertex in the grid, it'll slow me down to heck." And you'd be right - if it weren't for some tricks that can be performed. If you carefully examine the equation, you'll notice the first term contains a large amount of multiplication and division purely involving pre-defined constants - so all that has to be done is pre-compute it. The same applies for the second term and the third term, reducing a ton of multiplications, divisions and additions to one multiplication.
If you apply this function to the grid every frame, you'll find your water will sit there, perfectly still - as it should do. Now, try displacing one of the points by an amount - if you got your constants right the first time, you'll see some ripples form. If you got your constants wrong, you'll see hardly anything happen, or all the vertices in the grid will shoot off exponentially. You see, that's the problem with this algorithm; it has strict conditions as to when it will be stable, and when it will become unstable. These conditions can be expressed in some inequalities, and it's up to you to choose which one to try and satisfy. They are: or If one of these two conditions is not met, then as mentioned earlier, the vertices will shoot off at an exponential rate, which is definately not how we want our water to behave.
If, after all this, you're eager to write your own simulation, then good luck. If you want your ripples to 'bounce' off the edges of your grid rather than just dissipating, then always force the edge vertices to stay at a constant height - then it'll behave like a square puddle. Another thing you could do is try to have a variable grid - it'd still be a grid, but you could 'disable' some of the vertices to give it a different shape.
This concludes this whirlwind tour of a standard static water (and indeed, any fluid you care to mention) simulation. If you want more information on how the equations are derived, then head for chapter 12, fluid simulation, in 'Mathematics for 3D Game Programming & Computer Graphics'.