So how does this algorithm work then? Well, firstly, you'll need 2 arrays of words (integers). Yes, that's right, words, not bytes. These arrays will hold the state of the water. One holds the current state, the other holds the state from the previous frame. It is important that you have 2 arrays, since you need to know how the water has changed since the last frame, and the frame before that.
Data from the previous frame (Buffer2) and the frame before that (Buffer1) are used together, and
the results written into Buffer1. Buffer1 contains the current state of the water. |
Data from the previous frame (Buffer1) and the frame before that (Buffer2) are used together, and
the results written into Buffer2. Buffer2 contains the current state of the water. |
damping = some non-integer between 0 and 1 begin loop for every non-edge element: loop Buffer2(x, y) = (Buffer1(x-1,y) Buffer1(x+1,y) Buffer1(x,y+1) Buffer1(x,y-1)) / 2 - Buffer2(x,y) Buffer2(x,y) = Buffer2(x,y) * damping end loop Display Buffer2 Swap the buffers end loop |
Velocity(x, y) = -Buffer2(x, y)It is also important for the waves to spread out, so the buffers are smoothed every frame.
Smoothed(x,y) = (Buffer1(x-1, y) + Buffer1(x+1, y) + Buffer1(x, y-1) + Buffer1(x, y+1)) / 4Now, to combine the two to calculate the new height of the water. The multiplication by two reduces the effect of the velocity.
NewHeight(x,y) = Smoothed(x,y)*2 + Velocity(x,y)Finally, the ripples must lose energy, so they are damped:
NewHeight(x,y) = NewHeight(x,y) * dampingNote: This can be optimised to:
NewHeight(x,y) = NewHeight(x,y) - (NewHeight(x,y)/n)where n is some power of 2.
void ProcessWater(short *source, short *dest) { int i; for (i=320; i<64000-320; i++) { dest[i] = ( ((source[i-1]+ source[i+1]+ source[i-320]+ source[i+320]) >>1) ) -dest[i]; dest[i] -= (dest[i] >> 5); } } |
Rendering the Water
for every pixel (x,y) in the buffer Xoffset = buffer(x-1, y) - buffer(x+1, y) Yoffset = buffer(x, y-1) - buffer(x, y+1) Shading = Xoffset t = texture(x+Xoffset, y+Yoffset) p = t + Shading plot pixel at (x,y) with colour p end loop |
Notes: This method is by no means a highly accurate simulation of rippling water (though it is not too far off). Ripples can pass through each other quite happily, and will reflect off the walls. One thing you may notice, however, is that the ripples are not quite circular.
Here's a little demo of raindrops
falling into a rockpool. I have implemented shading and refraction to complete the effect.
As usual it requires DOS/4GW to run.