Water surface

an animated Gerstner sea refracting the scene below it.

▶ Run in browser

Tags: 3d water gerstner refraction transparent

A WaterSurface3D is a built-in transparent pass (its own shader pair, design D16), NOT a ShaderMaterial. Its vertex stage tessellates a grid and displaces it by a sum of four Gerstner waves whose direction + strength come from the WorldEnvironment wind and whose phase comes from the frame clock. Its fragment stage refracts the submerged geometry (sampling the scene colour/depth copy that the surface’s presence switches on), fades from a shallow tint to a deep tint with water depth, mixes a Fresnel reflection, and foams where the water meets the shore. Submerged pillars and a sloped floor show the depth fade, refraction wobble and shore foam; the wind drives the swell.

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

Source

  1"""Water surface: an animated Gerstner sea refracting the scene below it.
  2
  3A WaterSurface3D is a built-in transparent pass (its own shader pair, design
  4D16), NOT a ShaderMaterial. Its vertex stage tessellates a grid and displaces it
  5by a sum of four Gerstner waves whose direction + strength come from the
  6WorldEnvironment wind and whose phase comes from the frame clock. Its fragment
  7stage refracts the submerged geometry (sampling the scene colour/depth copy that
  8the surface's presence switches on), fades from a shallow tint to a deep tint
  9with water depth, mixes a Fresnel reflection, and foams where the water meets the
 10shore. Submerged pillars and a sloped floor show the depth fade, refraction
 11wobble and shore foam; the wind drives the swell.
 12
 13# /// simvx
 14# tags = ["3d", "water", "gerstner", "refraction", "transparent"]
 15# screenshot_frame = 40
 16# ///
 17
 18Usage:
 19    uv run python examples/features/3d/water.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    WaterMaterial,
 33    WaterSurface3D,
 34    WorldEnvironment,
 35)
 36from simvx.graphics import App
 37
 38WIDTH, HEIGHT = 1280, 720
 39
 40
 41class WaterScene(Node):
 42    def on_ready(self):
 43        InputMap.add_action("quit", [Key.ESCAPE])
 44
 45        # Post-processing + wind. Water refracts through the HDR scene copy, so
 46        # a WorldEnvironment (which enables the HDR chain) is required; the wind
 47        # drives the Gerstner swell direction + strength via FrameGlobals.
 48        env = self.add_child(WorldEnvironment())
 49        env.wind_direction = (0.7, 0.7)
 50        env.wind_strength = 0.6
 51
 52        self.add_child(Camera3D(position=(0, 3.2, 11.0), look_at=(0, 0.2, 0), up=(0, 1, 0)))
 53        sun = self.add_child(DirectionalLight3D(intensity=3.0))
 54        sun.direction = (-0.5, -1.0, -0.4)
 55
 56        # Sloped sandy floor descending away from the camera: gives a shoreline
 57        # (thin water -> foam) near the front and deep water at the back.
 58        floor = self.add_child(
 59            MeshInstance3D(
 60                mesh=Mesh.cube(1.0),
 61                material=Material(colour=(0.72, 0.62, 0.42, 1.0), roughness=0.9),
 62                position=(0, -2.2, -6.0),
 63                scale=(30.0, 0.5, 26.0),
 64            )
 65        )
 66        floor.rotation_degrees = (16.0, 0.0, 0.0)
 67
 68        # Submerged pillars: the refraction wobble + depth tint read clearly on
 69        # tall vertical shapes seen through the surface.
 70        for x, z, colour in [
 71            (-3.5, -1.0, (0.85, 0.3, 0.3, 1.0)),
 72            (0.0, -3.0, (0.3, 0.8, 0.45, 1.0)),
 73            (3.5, -1.5, (0.35, 0.5, 0.9, 1.0)),
 74        ]:
 75            self.add_child(
 76                MeshInstance3D(
 77                    mesh=Mesh.cube(1.0),
 78                    material=Material(colour=colour, roughness=0.6),
 79                    position=(x, -0.8, z),
 80                    scale=(0.9, 3.0, 0.9),
 81                )
 82            )
 83
 84        # A partly-emergent sphere island so the shoreline foam curves around it.
 85        self.add_child(
 86            MeshInstance3D(
 87                mesh=Mesh.sphere(1.6),
 88                material=Material(colour=(0.6, 0.55, 0.4, 1.0), roughness=0.85),
 89                position=(-1.5, -0.2, 2.0),
 90            )
 91        )
 92
 93        # The water plane at y=0. Its presence switches on the scene colour/depth
 94        # copy the refraction reads.
 95        self.water = self.add_child(WaterSurface3D(position=(0, 0, 0), size=(40.0, 40.0)))
 96        self.water.material = WaterMaterial(
 97            shallow_colour=(0.10, 0.42, 0.46),
 98            deep_colour=(0.01, 0.07, 0.14),
 99            depth_fade_distance=2.6,
100            wave_amplitude=0.16,
101            wave_length=6.5,
102            foam_amount=0.7,
103            refraction_strength=0.4,
104        )
105
106    def on_update(self, dt):
107        if Input.is_action_pressed("quit"):
108            self.app.quit()
109
110
111def main():
112    App(width=WIDTH, height=HEIGHT, title="SimVX - Water").run(WaterScene())
113
114
115if __name__ == "__main__":
116    main()