How to make an ocean shader: waves, Fresnel and foam

Updated 2026-10-08

A good ocean shader needs surprisingly little code. The look comes from five ideas stacked on top of each other: a height field of waves, a way to see it from a boat, reflection that depends on the viewing angle, a glittering sun, and foam. Here is how each one works.

1. The sea is a height field

For every spot (x, z) on the water, one number gives the height of the surface. Make it a sum of sine waves, each travelling in its own direction with its own frequency, speed and amplitude. Long slow swells with small fast ripples on top look natural; a single sine looks like a corrugated roof. See Ocean 1: a surface made of waves.

2. Seeing it from a boat

For each pixel, shoot a ray from the camera. Start where the ray crosses a flat water level, then repeat: measure how far you are above the real surface and walk that far along the ray (a little less, to avoid overshooting). A couple of dozen steps settle on the wave. The surface normal comes from measuring the height slightly left, right, in front and behind. Ocean 2: standing in the sea builds this.

3. Fresnel: water is a mirror at a low angle

Water has almost no color of its own: you mostly see the sky reflected in it, and how much depends on the angle you look at it. Look straight down and you see into the dark water; look at it nearly flat and it turns into a mirror. The standard approximation is 0.02 + 0.98 * pow(1.0 - dot(normal, -viewDir), 5.0). Mix the water color and the reflected sky with that number. See Ocean 3: sky, Fresnel and sun glitter.

4. Sun glitter

The sun's reflection is a tiny, very bright highlight (pow(max(dot(reflection, sun), 0.0), 400.0)). Because the wave normals change from pixel to pixel, it breaks into the glitter path you know from photographs of the sea.

5. Foam and haze

Whitecaps form on the tops of waves. Make foam where the height is large, but add a little noise to the height before the threshold so the edge is lacy and not a smooth blob. Finally, blend distant water towards the horizon color with 1.0 - exp(-distance * 0.012): this one line is what makes the picture feel big. See Ocean 4: foam, fine ripples and haze.

Where to go next

The same ray-marching approach draws volumetric clouds and smoke, and the wave-packet idea behind ripples is in Water simulation 2: rain on the pond. All the code runs in your browser: no engine needed, and you can export it to Unity, Unreal, Godot, WebGPU, Metal or GLSL when you are done.

Keep reading

Start the interactive lessons