Worley Sea

An efficient ocean scene with procedural water ripples and clouds.


Home Art Software Blog Research

See the full code in action here.

This project was my first attempt at rendering something using a height map, i.e. a function which takes a two dimensional position and outputs a height. The most common way to define a water height map is called "the sum of sines approximation." This technique is exactly what it sounds like, just adding a bunch of sine waves together. This ocean uses only two sine waves, I wanted to use something slightly more novel for the small ripples.

Worley noise is generated in essentially the same way as a Voronoi diagram. Start with a square grid, offset each vertex by some random values, for each point loop over the nearby vertices to determine the closest one then return the distance. This algorithm generates noise that looks like this:

If we zoom in we see something useful for creating ripples:

The ocean heightmap here is basically this pattern added to the two sine waves. But how did I get the ripples to be dynamic? Simple, perform the same algorithm but in three dimensions, then call that noise function with the two parameters for the heightmap and a third having to do with time.

The standard way of rendering a height map is to increment the length of a ray passing through a pixel until it hits the surface. Since the camera's position is fixed here, however, there are a couple nifty things we can do to make it run faster. The first is to only render the ocean if the direction of the ray is below the horizon. The second is to project onto a plane which we know the heightmap will be underneath, saving many valuable height map samples and ray increments.

What about the sky? Well, most of it just came about through experimentation, there were no special techniques or algorithms that I referenced - just intuition and trial and error. What I can explain is that the clouds are rendered in a somewhat similar way to the ripples in the water. Project the ray onto a plane (which is above the camera this time), then, instead of calling an ordinary noise function, call a 3D FBM function. FBM is noise that is scaled down and iteratively added to itself, it's good for making cloud-like patterns, which is why it's used here. What you see, however, isn't naked FBM it's a smoothstep of it based on a pre-defined cloudThickness parameter. The sun is just some jumbled exponentiation and interpolation based on the ray direction, and the fog is a similar idea.

Although it's not the most visually exciting scene, rendering Worley Sea was a big deal for me at the time that I did it because for a while I had been looking forward to the point where my skills would allow me to render an ocean.