Advanced shaders · lesson 2 of 19 · Advanced · about 5 min
Ocean 2: standing in the sea
You will build: Ray march the waves from a camera above the sea, light them with the sun, and fill the sky with a flat color.
Now we look at the water from a boat. For each pixel we shoot a ray from the camera and need to know where it meets the wave surface.
You know ray marching from the 3D lessons, but a height field allows a cheaper trick. Start at the point where the ray crosses a flat water level, then repeat: how far above the surface am I? q.y - waveH(q.xz). Move along the ray by about that much. When the waves are gentle this settles on the surface in a couple of dozen steps (a relaxed step, we only move 70% of the way so we never overshoot).
float t = min((ro.y - 0.3) / -rd.y, 80.0); // where the ray meets a flat level
for (int i = 0; i < 24; i++)
{
float3 q = ro + rd * t;
t += (q.y - waveH(q.xz)) * 0.7; // walk towards the real surface
}
float3 pos = ro + rd * t;
Lighting needs the normal. A height field makes that easy: measure the height a tiny bit to the left and right, and a tiny bit in front and behind. The slope gives the normal: (hL - hR, 2e, hD - hU), normalized.
Rays that point up never meet the water. They get a flat sky color for now.
The numbers you need
Everything the picture depends on is given, so the challenge is the code, not guessing.
- Camera
ro = float3(0.0, 2.2, -4.0), rd = normalize(float3(p.x, p.y - 0.35, 1.6)), p = (uv - 0.5) * float2(aspect, 1.0) * 2.0- Sun
normalize(float3(-0.3, 0.25, 0.92))- Sky
float3(0.62, 0.78, 0.95) for every ray- Hits the water if
rd.y < -0.01- Start
t = min((ro.y - 0.3) / -rd.y, 80.0), then 24 times: q = ro + rd * t; t += (q.y - waveH(q.xz)) * 0.7- Normal
e = 0.03; n = normalize(float3(h(x - e) - h(x + e), 2.0 * e, h(z - e) - h(z + e))) (h = waveH at pos.xz shifted)- Water color
float3(0.03, 0.2, 0.28) * (0.4 + 0.9 * max(dot(n, sun), 0.0))
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