FFT ocean

an open sea driven by a Tessendorf spectrum, not analytic waves.

▶ Run in browser

Tags: 3d ocean fft water transparent

An OceanSurface3D is a built-in transparent pass (design D11) that reuses the WaterMaterial shading of the Gerstner water pass but replaces the analytic vertex displacement with a sampled FFT displacement map: a Phillips spectrum with the WorldEnvironment wind, inverse-transformed each frame into per-cascade displacement, slope and foam. Three cascades (long swell / mid chop / fine ripple) sum into a believable open sea: rolling swell, choppy crests, foam laced onto the steep folding faces, sun glint tracking the wind, and depth-faded refraction of the submerged island. The cascade texture size + count come from the graphics quality tier.

Usage: uv run python examples/features/3d/ocean.py

Source

  1"""FFT ocean: an open sea driven by a Tessendorf spectrum, not analytic waves.
  2
  3An OceanSurface3D is a built-in transparent pass (design D11) that reuses the
  4WaterMaterial shading of the Gerstner water pass but replaces the analytic vertex
  5displacement with a sampled FFT displacement map: a Phillips spectrum with the
  6WorldEnvironment wind, inverse-transformed each frame into per-cascade
  7displacement, slope and foam. Three cascades (long swell / mid chop / fine
  8ripple) sum into a believable open sea: rolling swell, choppy crests, foam laced
  9onto the steep folding faces, sun glint tracking the wind, and depth-faded
 10refraction of the submerged island. The cascade texture size + count come from
 11the graphics quality tier.
 12
 13# /// simvx
 14# tags = ["3d", "ocean", "fft", "water", "transparent"]
 15# screenshot_frame = 70
 16# ///
 17
 18Usage:
 19    uv run python examples/features/3d/ocean.py
 20"""
 21
 22from simvx.core import (
 23    Camera3D,
 24    DirectionalLight3D,
 25    Input,
 26    InputMap,
 27    Key,
 28    Material,
 29    Mesh,
 30    MeshInstance3D,
 31    Node,
 32    OceanSurface3D,
 33    WaterMaterial,
 34    WorldEnvironment,
 35)
 36from simvx.graphics import App
 37
 38WIDTH, HEIGHT = 1280, 720
 39
 40
 41class OceanScene(Node):
 42    def on_ready(self):
 43        InputMap.add_action("quit", [Key.ESCAPE])
 44
 45        # The ocean refracts through the HDR scene copy, so a WorldEnvironment
 46        # (which enables the HDR chain) is required; its wind drives the Phillips
 47        # spectrum direction + strength via FrameGlobals.
 48        env = self.add_child(WorldEnvironment())
 49        env.wind_direction = (0.8, 0.35)
 50        env.wind_strength = 0.7
 51
 52        self.add_child(Camera3D(position=(0, 5.0, 20.0), look_at=(0, 1.5, -30.0), up=(0, 1, 0)))
 53        sun = self.add_child(DirectionalLight3D(intensity=3.2))
 54        sun.direction = (-0.6, -0.7, -0.5)
 55
 56        # A rocky island breaking the surface: gives the shoreline foam a shape to
 57        # curl around and submerged geometry for the depth-fade + refraction to read.
 58        self.add_child(
 59            MeshInstance3D(
 60                mesh=Mesh.sphere(6.0),
 61                material=Material(colour=(0.42, 0.38, 0.32, 1.0), roughness=0.9),
 62                position=(-6.0, -1.5, -14.0),
 63            )
 64        )
 65        # A couple of half-submerged pillars near the camera for refraction wobble.
 66        for x, z in [(5.0, -6.0), (9.0, -12.0)]:
 67            self.add_child(
 68                MeshInstance3D(
 69                    mesh=Mesh.cube(1.0),
 70                    material=Material(colour=(0.30, 0.28, 0.26, 1.0), roughness=0.7),
 71                    position=(x, -0.5, z),
 72                    scale=(1.4, 4.0, 1.4),
 73                )
 74            )
 75
 76        # The open-sea plane at y=0. Its presence switches on the scene
 77        # colour/depth copy the refraction reads. Big + finely tessellated so the
 78        # FFT cascades resolve into swell + chop across the view.
 79        self.ocean = self.add_child(OceanSurface3D(position=(0, 0, 0), size=(800.0, 800.0), subdivisions=320))
 80        self.ocean.material = WaterMaterial(
 81            shallow_colour=(0.06, 0.30, 0.36),
 82            deep_colour=(0.01, 0.06, 0.12),
 83            depth_fade_distance=8.0,
 84            wave_amplitude=1.4,
 85            wave_steepness=0.75,
 86            foam_colour=(0.92, 0.96, 1.0),
 87            foam_amount=1.2,
 88            refraction_strength=0.4,
 89        )
 90
 91    def on_update(self, dt):
 92        if Input.is_action_pressed("quit"):
 93            self.app.quit()
 94
 95
 96def main():
 97    App(width=WIDTH, height=HEIGHT, title="SimVX - FFT Ocean").run(OceanScene())
 98
 99
100if __name__ == "__main__":
101    main()