GLSL shader example
Interactive Heightfield Water Ripples
A two-time-level finite-difference wave simulation with click impulses. Two bytes per height reduce quantization drift on RGBA8 targets.
Live example
Edit the code and the preview updates as you type.
What to observe
Interactive Heightfield Water Ripples.
Mechanism
R/G encode current height in two bytes and B/A encode the previous height; a four-neighbor Laplacian advances a discrete wave equation. Click to disturb it. Two-byte height encoding reduces quantization of small waves on RGBA8 targets.
The controlling code
float unpackHeight(vec2 bytes){return ((bytes.x*255.0*256.0+bytes.y*255.0)/65535.0-0.5)*2.0;}
vec2 px=1./u_resolution;float dx=unpackHeight(texture2D(u_water,uv+vec2(px.x,0)).rg)-unpackHeight(texture2D(u_water,uv-vec2(px.x,0)).rg);float dy=unpackHeight(texture2D(u_water,uv+vec2(0,px.y)).rg)-unpackHeight(texture2D(u_water,uv-vec2(0,px.y)).rg);vec3 n=normalize(vec3(dx*30.,dy*30.,1.));c=vec3(.03,.25,.4)+max(dot(n,normalize(vec3(.5,.8,1.))),0.)*.4;
u_water packs current height into R/G and previous height into B/A, so its raw colors are encoded data. In Fragment, use c=vec3(unpackHeight(texture2D(u_water,uv).rg)*.5+.5) to inspect height as grayscale instead of shaded normals.
Experiment
In u_water, lower the damping factor in (2.*h-prev+lap*.24)*.99 from .99 to .97. Click while running: waves lose strength faster, although the Laplacian coefficient and propagation rule stay the same.
Debugging and cost
u_water reads the center and four neighbors, while the display takes four height samples for its normal. Change both dx*30. and dy*30. to *15. in Fragment to flatten the apparent lighting without changing the stored wave heights.