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), 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.

Controls: ESC: Quit

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), NOT a
  4ShaderMaterial. 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
 18Controls:
 19    ESC: Quit
 20
 21Usage:
 22    uv run python examples/features/3d/water.py
 23"""
 24
 25from simvx.core import (
 26    Camera3D,
 27    DirectionalLight3D,
 28    Input,
 29    InputMap,
 30    Key,
 31    Material,
 32    Mesh,
 33    MeshInstance3D,
 34    Node,
 35    WaterMaterial,
 36    WaterSurface3D,
 37    WorldEnvironment,
 38)
 39from simvx.graphics import App
 40
 41WIDTH, HEIGHT = 1280, 720
 42
 43
 44class WaterScene(Node):
 45    def on_ready(self):
 46        InputMap.add_action("quit", [Key.ESCAPE])
 47
 48        # Post-processing + wind. Water refracts through the HDR scene copy, so
 49        # a WorldEnvironment (which enables the HDR chain) is required; the wind
 50        # drives the Gerstner swell direction + strength via FrameGlobals.
 51        env = self.add_child(WorldEnvironment())
 52        env.wind_direction = (0.7, 0.7)
 53        env.wind_strength = 0.6
 54
 55        self.add_child(Camera3D(position=(0, 3.2, 11.0), look_at=(0, 0.2, 0), up=(0, 1, 0)))
 56        sun = self.add_child(DirectionalLight3D(intensity=3.0))
 57        sun.direction = (-0.5, -1.0, -0.4)
 58
 59        # Sloped sandy floor descending away from the camera: gives a shoreline
 60        # (thin water -> foam) near the front and deep water at the back.
 61        floor = self.add_child(
 62            MeshInstance3D(
 63                mesh=Mesh.cube(1.0),
 64                material=Material(colour=(0.72, 0.62, 0.42, 1.0), roughness=0.9),
 65                position=(0, -2.2, -6.0),
 66                scale=(30.0, 0.5, 26.0),
 67            )
 68        )
 69        floor.rotation_degrees = (16.0, 0.0, 0.0)
 70
 71        # Submerged pillars: the refraction wobble + depth tint read clearly on
 72        # tall vertical shapes seen through the surface.
 73        for x, z, colour in [
 74            (-3.5, -1.0, (0.85, 0.3, 0.3, 1.0)),
 75            (0.0, -3.0, (0.3, 0.8, 0.45, 1.0)),
 76            (3.5, -1.5, (0.35, 0.5, 0.9, 1.0)),
 77        ]:
 78            self.add_child(
 79                MeshInstance3D(
 80                    mesh=Mesh.cube(1.0),
 81                    material=Material(colour=colour, roughness=0.6),
 82                    position=(x, -0.8, z),
 83                    scale=(0.9, 3.0, 0.9),
 84                )
 85            )
 86
 87        # A partly-emergent sphere island so the shoreline foam curves around it.
 88        self.add_child(
 89            MeshInstance3D(
 90                mesh=Mesh.sphere(1.6),
 91                material=Material(colour=(0.6, 0.55, 0.4, 1.0), roughness=0.85),
 92                position=(-1.5, -0.2, 2.0),
 93            )
 94        )
 95
 96        # The water plane at y=0. Its presence switches on the scene colour/depth
 97        # copy the refraction reads.
 98        self.water = self.add_child(WaterSurface3D(position=(0, 0, 0), size=(40.0, 40.0)))
 99        self.water.material = WaterMaterial(
100            shallow_colour=(0.10, 0.42, 0.46),
101            deep_colour=(0.01, 0.07, 0.14),
102            depth_fade_distance=2.6,
103            wave_amplitude=0.16,
104            wave_length=6.5,
105            foam_amount=0.7,
106            refraction_strength=0.4,
107        )
108
109    def on_update(self, dt):
110        if Input.is_action_pressed("quit"):
111            self.app.quit()
112
113
114def main():
115    App(width=WIDTH, height=HEIGHT, title="SimVX - Water").run(WaterScene())
116
117
118if __name__ == "__main__":
119    main()