Rain and wetness¶
a GPU downpour over a rain-slicked courtyard.
▶ Run in browserTags: 3d rain wetness weather particles
Two cooperating halves of the RM-E3 weather system, both driven by one
WorldEnvironment. A Rain3D emitter (a camera-relative GPU particle volume that
reuses the particle simulation) fills the view with wind-slanted droplets. The
same WorldEnvironment publishes wetness / rain_intensity /
ripple_strength into the FrameGlobals block, so every material flagged
wetness_affected darkens, turns glossy (roughness drops), and grows an
animated ripple normal on its up-facing faces. Turn the weather off (all three
back to zero, no Rain3D) and the scene renders exactly as a dry one: the wet path
is feature-bit + wetness gated, so it costs nothing when unused.
The wide floor and the low blocks are wetness_affected; the sun rakes across
them so the wet gloss and the puddle ripples catch the light, while the drops
slant with the wind.
Usage: uv run python examples/features/3d/rain.py
Source¶
1"""Rain and wetness: a GPU downpour over a rain-slicked courtyard.
2
3Two cooperating halves of the RM-E3 weather system, both driven by one
4WorldEnvironment. A Rain3D emitter (a camera-relative GPU particle volume that
5reuses the particle simulation) fills the view with wind-slanted droplets. The
6same WorldEnvironment publishes ``wetness`` / ``rain_intensity`` /
7``ripple_strength`` into the FrameGlobals block, so every material flagged
8``wetness_affected`` darkens, turns glossy (roughness drops), and grows an
9animated ripple normal on its up-facing faces. Turn the weather off (all three
10back to zero, no Rain3D) and the scene renders exactly as a dry one: the wet path
11is feature-bit + wetness gated, so it costs nothing when unused.
12
13The wide floor and the low blocks are ``wetness_affected``; the sun rakes across
14them so the wet gloss and the puddle ripples catch the light, while the drops
15slant with the wind.
16
17# /// simvx
18# tags = ["3d", "rain", "wetness", "weather", "particles"]
19# screenshot_frame = 40
20# ///
21
22Usage:
23 uv run python examples/features/3d/rain.py
24"""
25
26from simvx.core import (
27 Camera3D,
28 DirectionalLight3D,
29 Input,
30 InputMap,
31 Key,
32 Material,
33 Mesh,
34 MeshInstance3D,
35 Node,
36 Rain3D,
37 WorldEnvironment,
38)
39from simvx.graphics import App
40
41WIDTH, HEIGHT = 1280, 720
42
43
44class RainScene(Node):
45 def on_ready(self):
46 InputMap.add_action("quit", [Key.ESCAPE])
47
48 # One WorldEnvironment drives both halves: it enables the HDR chain, sets
49 # the wind that slants the rain, and publishes the wetness/rain/ripple
50 # weather values into FrameGlobals that the wet materials read.
51 env = self.add_child(WorldEnvironment())
52 env.wind_direction = (0.6, 0.8)
53 env.wind_strength = 0.5
54 env.wetness = 0.9
55 env.rain_intensity = 0.85
56 env.ripple_strength = 0.7
57
58 self.add_child(Camera3D(position=(0, 3.4, 9.5), look_at=(0, 0.3, -1.0), up=(0, 1, 0)))
59 sun = self.add_child(DirectionalLight3D(intensity=3.2))
60 sun.direction = (-0.55, -0.85, -0.3)
61
62 # Rain-slicked courtyard floor: a wetness_affected material darkens and
63 # turns glossy under the rain, and its up-facing top carries the ripples.
64 self.add_child(
65 MeshInstance3D(
66 mesh=Mesh.cube(1.0),
67 material=Material(colour=(0.55, 0.55, 0.58, 1.0), roughness=0.8, wetness_affected=True),
68 position=(0, -0.5, -4.0),
69 scale=(28.0, 1.0, 26.0),
70 )
71 )
72
73 # Low blocks scattered across the floor: also wet, so their tops gloss and
74 # ripple while their sides just darken (the ripple is up-facing only).
75 for x, z, colour in [
76 (-3.2, -2.0, (0.70, 0.30, 0.28)),
77 (2.8, -3.5, (0.30, 0.55, 0.72)),
78 (0.2, -5.5, (0.60, 0.58, 0.35)),
79 (-1.6, -6.5, (0.40, 0.62, 0.42)),
80 ]:
81 self.add_child(
82 MeshInstance3D(
83 mesh=Mesh.cube(1.0),
84 material=Material(colour=(*colour, 1.0), roughness=0.75, wetness_affected=True),
85 position=(x, 0.4, z),
86 scale=(1.6, 0.8, 1.6),
87 )
88 )
89
90 # The downpour: a camera-relative GPU particle volume, wind-slanted.
91 self.add_child(Rain3D(radius=16.0, height=10.0, fall_speed=24.0, amount=8000))
92
93 def on_update(self, dt):
94 if Input.is_action_pressed("quit"):
95 self.app.quit()
96
97
98def main():
99 App(width=WIDTH, height=HEIGHT, title="SimVX - Rain").run(RainScene())
100
101
102if __name__ == "__main__":
103 main()