Advanced shaders · lesson 11 of 19 · Advanced · about 5 min
Snow 1: a field of snow
You will build: Draw rolling snow dunes with lumpy noise, lit by a low sun with blue shadows, fading into the sky with fog.
Snow is a height field too, like the sea, but a frozen one. Rolling dunes from a couple of sines, plus noise at two sizes for the lumpy texture: big lumps (noise2(xz * 1.7)) and tiny grain (noise2(xz * 7.0)).
We march a ray over the surface with the same relaxed-step trick as the ocean, but now we stop as soon as we are within a hair of the snow (d < 0.002), and remember that we hit it.
The light is what makes snow look like snow, and it has two rules:
- The sun is low, so the bumps cast long soft shading:
max(dot(n, sun), 0.0). - Shadows are not grey, they are blue. Snow in shadow is lit by the blue sky, so blend from a blue shadow color to a warm white:
lerp(shadow, lit, diff). This one choice is what makes it read as snow.
Distant snow melts into the sky with a bit of fog.
float diff = max(dot(n, sun), 0.0); float3 shadow = float3(0.55, 0.66, 0.88); // blue, from the sky float3 lit = float3(1.0, 0.97, 0.92); // warm, from the sun col = float3(0.93, 0.96, 1.0) * lerp(shadow, lit, diff);
The numbers you need
Everything the picture depends on is given, so the challenge is the code, not guessing.
- Height
snowH(xz) = 0.16 * sin(xz.x * 0.35 + 0.8) * sin(xz.y * 0.28) + 0.05 * noise2(xz * 1.7) + 0.012 * noise2(xz * 7.0)- Camera
ro = float3(0.0, 1.3, -3.2), rd = normalize(float3(p.x, p.y - 0.6, 1.5)), p = (uv - 0.5) * float2(aspect, 1.0) * 2.0- Sun
normalize(float3(-0.6, 0.35, -0.3))- Sky
lerp(float3(0.78, 0.86, 0.96), float3(0.35, 0.55, 0.85), saturate(rd.y * 2.0 + 0.2))- March
t = 0.5; up to 64 steps: pos = ro + rd * t; d = pos.y - snowH(pos.xz); if d < 0.002 it is a hit (stop); t += d * 0.5; stop if t > 40.0- Normal
e = 0.02; normalize(float3(h(x - e) - h(x + e), 2.0 * e, h(z - e) - h(z + e)))- Snow color
float3(0.93, 0.96, 1.0) * lerp(float3(0.55, 0.66, 0.88), float3(1.0, 0.97, 0.92), max(dot(n, sun), 0.0))- Fog
lerp(col, sky, 1.0 - exp(-t * 0.035))
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 codingGo deeper
Part of the learning path: Game effects: smoke, fire and snow.