Advanced shaders · lesson 19 of 19 · Advanced · about 5 min

Water simulation 2: rain on the pond

You will build: Sum the ripples of eight drops at random places and times. Rings from different drops should cross and add up.

The picture produced by the shader in the lesson "Water simulation 2: rain on the pond"
The target picture. In the lesson you write the shader that draws it, and a match bar shows how close you are.

One drop is a wave packet. Rain is a lot of drops at different places and different moments, and the best part of water is what happens when the rings meet: they pass through each other and add up, making the bright and dark crossings you see on a real pond.

In the shader that is just a sum: the height is the sum of the heights of all drops. A for loop runs over 8 drops, and each needs its own random place and its own timing.

  • Place: hash21(float2(fi, 1.3)) and hash21(float2(fi, 7.7)) are two different random numbers per drop number fi, mapped to the pond with - 0.5 and a scale.
  • Timing: every drop repeats every 4 seconds, but with its own offset hash21(float2(fi, 3.1)) * 4.0, so they fall out of step.

Everything else (the slope, the refraction, the sparkle) is exactly the same as the one-drop pond, because it only reads the height() function. That is the great thing about describing the surface as a function: the lighting does not care how complicated the surface is.

float h = 0.0;
for (int i = 0; i < 8; i++)
{
    float fi = float(i);
    float2 c = float2(hash21(float2(fi, 1.3)) - 0.5, hash21(float2(fi, 7.7)) - 0.5) * float2(1.5, 1.0);
    float age = fmod(time + hash21(float2(fi, 3.1)) * 4.0, 4.0);
    // ...the wave packet from last time, at distance length(p - c)...
    h += wave * 0.016 * step(0.0, x + 0.12);
}

The numbers you need

Everything the picture depends on is given, so the challenge is the code, not guessing.

Drop i
fi = float(i), centre c = float2(hash21(float2(fi, 1.3)) - 0.5, hash21(float2(fi, 7.7)) - 0.5) * float2(1.5, 1.0)
Timing
age = fmod(time + hash21(float2(fi, 3.1)) * 4.0, 4.0)
Ring
r = length(p - c); x = r - age * 0.28; wave = sin(x * 55.0) * exp(-x * x * 140.0) * exp(-age * 0.75)
Sum
h += wave * 0.016 * step(0.0, x + 0.12), for 8 drops
Everything else
the shading from Water simulation 1

Try it in your own language

The lesson is written once and shown in every language. Pick yours: Unity (Built-in), Unity (URP), Unreal, Shadertoy / WebGL, OpenGL (LWJGL, raylib), three.js, GameMaker, LÖVE (Lua), Godot, Apple (iOS, macOS), WebGPU / Bevy.

Start coding