Water surface¶
an animated Gerstner sea refracting the scene below it.
▶ Run in browserTags: 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()