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

Snow 2: footprints

You will build: Add ten footprints to the height function: oval pits with raised rims, left and right feet turned a little, appearing one after another over time.

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

How do you dig footprints into a surface that has no memory? You do not store them: you describe them. A walker's path is a rule (here: steps 0.8 apart along z, left foot at x = -0.26, right foot at +0.26), so footprint number i is at a position you can compute. Then it is one more term in the height function.

A footprint is an oval, so its distance from the centre is the length of the squashed offset: e = length(q / float2(0.11, 0.26)). Inside the oval e < 1.

  • The pit: lower the snow where e is small: h -= 0.09 * (1.0 - smoothstep(0.55, 1.0, e)) (a flat bottom with a sloped edge).
  • The rim: the pressed snow bulges up just outside the oval: a narrow bump 0.025 * exp(-pow((e - 1.15) * 5.0, 2.0)) (a Gaussian bump centered at e = 1.15).

Each foot is turned a little (opposite directions for left and right) with a small rotation. And footprints appear one after the other as the walker moves away from the camera: footprint i exists once time * 1.1 has passed i * 0.45. step(fi * 0.45, time * 1.1) is 0 before that and 1 after.

float side = fmod(fi, 2.0) * 2.0 - 1.0;        // -1 for left feet, +1 for right feet
float2 c = float2(side * 0.26, -1.2 + fi * 0.8);
float e = length(q / float2(0.11, 0.26));      // q = offset from c, slightly turned
float born = step(fi * 0.45, time * 1.1);      // has this step happened yet?
h -= born * 0.09 * (1.0 - smoothstep(0.55, 1.0, e));
h += born * 0.025 * exp(-pow((e - 1.15) * 5.0, 2.0));

The numbers you need

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

Loop
inside snowH, after the dunes: for i = 0 to 9, fi = float(i)
Foot
side = fmod(fi, 2.0) * 2.0 - 1.0; c = float2(side * 0.26, -1.2 + fi * 0.8)
Turned offset
q = xz - c; ca = cos(side * 0.2); sa = sin(side * 0.2); q = float2(ca * q.x + sa * q.y, -sa * q.x + ca * q.y)
Oval distance
e = length(q / float2(0.11, 0.26))
Appears
born = step(fi * 0.45, time * 1.1)
Pit
h -= born * 0.09 * (1.0 - smoothstep(0.55, 1.0, e))
Rim
h += born * 0.025 * exp(-pow((e - 1.15) * 5.0, 2.0))
Everything else
as in Snow 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

Go deeper

Part of the learning path: Game effects: smoke, fire and snow.