Water Surface.¶
An animated sea over a sloped sandy floor, with three pillars standing in the water and a half-sunk sphere forming an island.
▶ Run in browserTags: 3d water gerstner refraction transparent
WaterSurface3D is a built-in render pass, not a ShaderMaterial: place the node at the water
level and give it a size (x, z) in world units. Its vertex stage displaces a grid by four
Gerstner waves, and its fragment stage refracts whatever is below the surface, tints it from
shallow_colour to deep_colour over depth_fade_distance, blends in a Fresnel reflection of
the environment, and draws foam where the water column is thinner than foam_amount. The
appearance lives on a WaterMaterial passed as the node’s material.
The waves follow the wind: WorldEnvironment.wind_direction sets their heading and
wind_strength scales their height. With no wind the material’s own wave_direction and
wave_amplitude drive them, so water moves out of the box.
Here the floor rises away from the camera into a beach, so the water is deep and dark in front
and shoals to foam along the far shore; the pillars show the refraction wobble and depth tint,
and the island shows foam curving around its shore. For a true mirror of the world above the
surface, see planar_water.py.
Source¶
1# /// simvx
2# tags = ["3d", "water", "gerstner", "refraction", "transparent"]
3# screenshot_frame = 40
4# ///
5"""Water Surface.
6
7An animated sea over a sloped sandy floor, with three pillars standing in the water and a
8half-sunk sphere forming an island.
9
10`WaterSurface3D` is a built-in render pass, not a `ShaderMaterial`: place the node at the water
11level and give it a `size` (x, z) in world units. Its vertex stage displaces a grid by four
12Gerstner waves, and its fragment stage refracts whatever is below the surface, tints it from
13`shallow_colour` to `deep_colour` over `depth_fade_distance`, blends in a Fresnel reflection of
14the environment, and draws foam where the water column is thinner than `foam_amount`. The
15appearance lives on a `WaterMaterial` passed as the node's `material`.
16
17The waves follow the wind: `WorldEnvironment.wind_direction` sets their heading and
18`wind_strength` scales their height. With no wind the material's own `wave_direction` and
19`wave_amplitude` drive them, so water moves out of the box.
20
21Here the floor rises away from the camera into a beach, so the water is deep and dark in front
22and shoals to foam along the far shore; the pillars show the refraction wobble and depth tint,
23and the island shows foam curving around its shore. For a true mirror of the world above the
24surface, see `planar_water.py`.
25"""
26
27from simvx.core import (
28 Camera3D,
29 DirectionalLight3D,
30 Material,
31 Mesh,
32 MeshInstance3D,
33 Node,
34 WaterMaterial,
35 WaterSurface3D,
36 WorldEnvironment,
37)
38from simvx.graphics import App
39
40PILLARS = [
41 ((-3.5, -0.8, -1.0), (0.85, 0.3, 0.3, 1.0)),
42 ((0.0, -0.8, -3.0), (0.3, 0.8, 0.45, 1.0)),
43 ((3.5, -0.8, -1.5), (0.35, 0.5, 0.9, 1.0)),
44]
45
46
47class WaterScene(Node):
48 def on_ready(self):
49 self.add_child(WorldEnvironment(wind_direction=(0.7, 0.7), wind_strength=0.6))
50 self.add_child(Camera3D(position=(0, 3.2, 11.0), look_at=(0, 0.2, 0)))
51 self.add_child(DirectionalLight3D(intensity=3.0, direction=(-0.5, -1.0, -0.4)))
52
53 sand = Material(colour=(0.72, 0.62, 0.42, 1.0), roughness=0.9)
54 floor = self.add_child(
55 MeshInstance3D(mesh=Mesh.box((30.0, 0.5, 26.0)), material=sand, position=(0, -2.2, -6.0))
56 )
57 floor.rotation_degrees = (16.0, 0.0, 0.0)
58
59 for position, colour in PILLARS:
60 pillar = Material(colour=colour, roughness=0.6)
61 self.add_child(MeshInstance3D(mesh=Mesh.box((0.9, 3.0, 0.9)), material=pillar, position=position))
62
63 rock = Material(colour=(0.6, 0.55, 0.4, 1.0), roughness=0.85)
64 self.add_child(MeshInstance3D(mesh=Mesh.sphere(1.6), material=rock, position=(-1.5, -0.2, 2.0)))
65
66 water = WaterMaterial(
67 shallow_colour=(0.10, 0.42, 0.46),
68 deep_colour=(0.01, 0.07, 0.14),
69 depth_fade_distance=2.6,
70 wave_amplitude=0.16,
71 wave_length=6.5,
72 foam_amount=0.7,
73 refraction_strength=0.4,
74 )
75 self.add_child(WaterSurface3D(material=water, size=(40.0, 40.0)))
76
77
78if __name__ == "__main__":
79 App(title="Water Surface", width=1280, height=720).run(WaterScene())