Ollin

Ocean

A sea built from its own wave spectrum. One inverse Fourier transform on the GPU makes the surface, which is drawn as water with no geometry anywhere (makeOceanField, drawOcean).

Run
swift run --package-path Examples Example-3D-Geometry-Ocean
Source
Examples/3D/Geometry/Ocean/Sketch.swift
import Ollin

/// A sea, made the way the ocean is measured.
///
/// Nothing here places a wave. `makeOceanField(_:)` writes a *spectrum* on the GPU,
/// which says how much water stands at each wavelength and heading for this
/// wind, and one inverse Fourier transform turns that whole field of
/// frequencies into the moving surface in a single step. What comes back is a
/// layer: how far each point of the water has moved sideways, how high it
/// stands, and how hard it is folding over on itself, which is where foam goes.
///
/// `drawOcean(_:)` then draws it as water with no geometry anywhere: each
/// vertex works out which corner of which cell it is from its own index and
/// reads the field for where the wave has carried it.
///
/// `waveHeight` is in world units and means what a sailor means by it, the
/// average of the tallest third, so the parameter is a measurement rather than a
/// dial to turn by eye. `windSpeed` moves the energy between short chop and
/// long swell without changing that height, and `choppiness` moves water
/// toward the crests, which is what makes them narrow and the troughs wide.
@main
final class OceanSurface: Sketch {

    @Param(0.2 ... 8, icon: "water.waves") var waveHeight = 2.6
    @Param(5 ... 26, icon: "wind") var windSpeed = 12.0
    @Param(0 ... 1.6, icon: "triangle") var choppiness = 1.25
    @Param(0 ... 2.5, icon: "cloud.fog") var foam = 1.2
    @Param(icon: "sun.horizon") var lowSun = true

    override func draw() {
        background(Color(hex: 0x8FB6D4))
        toneMap(.aces)

        // The sun stands in a fixed compass direction (+Z) at this elevation,
        // and the same elevation aims the light, so the sparkle on the water
        // and the sun in the sky are the same sun.
        let elevation = lowSun ? 0.13 : 0.55
        environment(.sky(turbidity: 2.6, sunElevation: elevation))
        light(.directional(Color(hex: 0xFFF1DC),
                           direction: Vector3(0, -sin(elevation), -cos(elevation)),
                           intensity: 1.1))

        // Low over the water, looking down the sparkle path toward the sun.
        camera(.perspective(eye: Vector3(0, 4.6, -95), target: Vector3(0, 2.6, 220),
                            fieldOfView: .pi / 3.4))

        let sea = makeOceanField(Ocean(waveHeight: waveHeight,
                                   windSpeed: windSpeed,
                                   windDirection: 90,
                                   choppiness: choppiness,
                                   patchSize: 170,
                                   smallestWave: 0.6,
                                   seed: 7), resolution: 512)

        var look = WaterSurface.open
        look.foamAmount = foam
        drawOcean(sea, segments: 360, tiles: 7, water: look)
    }
}

↑↓ move ↩ open