Dynamic Sky¶
a procedural day-night sky that lights the scene.
▶ Run in browserTags: 3d sky procedural preetham ibl lighting
Set sky_mode="procedural" on a WorldEnvironment and the renderer builds
the sky itself from the Preetham analytic model, with no cubemap asset to load:
WorldEnvironment(sky_mode="procedural", sky_turbidity=2.6)
The sun is the scene’s first DirectionalLight3D. The sky places its sun disc
opposite the light’s direction (which points the way the light travels), so
turning the light moves the sun, colours the horizon and, once the sun is below
it, fades the sky to night. sky_turbidity is the haze, from about 2 for a
clear day to 10 for a hazy one, and sky_ground_albedo tints the light bounced
up from the ground.
The same sky drives the image-based lighting. The four spheres share a white metal and differ only in roughness: the smooth one mirrors the sky and the sun’s glint, the rough one takes a soft wash of its colour. As the sun moves, the sky and its lighting are rebuilt in small steps rather than every frame.
Here the sun rises ahead of the camera, passes over the top and sets behind it, then the sky turns to night before the next dawn. Press Space, click or tap to pause and resume the sweep.
Source¶
1# /// simvx
2# tags = ["3d", "sky", "procedural", "preetham", "ibl", "lighting"]
3# screenshot_frame = 44
4# ///
5"""Dynamic Sky: a procedural day-night sky that lights the scene.
6
7Set ``sky_mode="procedural"`` on a ``WorldEnvironment`` and the renderer builds
8the sky itself from the Preetham analytic model, with no cubemap asset to load:
9
10 WorldEnvironment(sky_mode="procedural", sky_turbidity=2.6)
11
12The sun is the scene's first ``DirectionalLight3D``. The sky places its sun disc
13opposite the light's ``direction`` (which points the way the light travels), so
14turning the light moves the sun, colours the horizon and, once the sun is below
15it, fades the sky to night. ``sky_turbidity`` is the haze, from about 2 for a
16clear day to 10 for a hazy one, and ``sky_ground_albedo`` tints the light bounced
17up from the ground.
18
19The same sky drives the image-based lighting. The four spheres share a white
20metal and differ only in roughness: the smooth one mirrors the sky and the sun's
21glint, the rough one takes a soft wash of its colour. As the sun moves, the sky
22and its lighting are rebuilt in small steps rather than every frame.
23
24Here the sun rises ahead of the camera, passes over the top and sets behind it,
25then the sky turns to night before the next dawn. Press Space, click or tap to
26pause and resume the sweep.
27"""
28
29import math
30
31from simvx.core import (
32 Anchor,
33 Camera3D,
34 CanvasLayer,
35 DirectionalLight3D,
36 Key,
37 Label,
38 Material,
39 Mesh,
40 MeshInstance3D,
41 Node,
42 Pointer,
43 Vec3,
44 WorldEnvironment,
45 on_input,
46)
47from simvx.graphics import App
48
49SWEEP_SPEED = 0.3 # radians of sun arc per second
50
51
52class DynamicSkyScene(Node):
53 input_context = {"pause": [Key.SPACE, Pointer.PRIMARY]}
54
55 def on_ready(self):
56 self.phase = 0.35
57 self.paused = False
58 self.add_child(
59 WorldEnvironment(
60 sky_mode="procedural",
61 sky_turbidity=2.6,
62 sky_ground_albedo=(0.16, 0.15, 0.13, 1.0),
63 bloom_enabled=True,
64 bloom_threshold=1.1,
65 bloom_intensity=0.5,
66 )
67 )
68 self.add_child(Camera3D(position=(0, 2.4, 8.5), look_at=(0, 0.7, 0)))
69 self.sun = self.add_child(DirectionalLight3D(intensity=3.2))
70 self.place_sun()
71
72 self.add_child(
73 MeshInstance3D(
74 mesh=Mesh.box((24, 0.4, 24)),
75 material=Material(colour=(0.38, 0.4, 0.42), roughness=0.85),
76 position=(0, -0.8, 0),
77 )
78 )
79 sphere = Mesh.sphere(1.0)
80 for i, roughness in enumerate((0.05, 0.2, 0.45, 0.75)):
81 material = Material(colour=(0.95, 0.95, 0.96), metallic=1.0, roughness=roughness)
82 self.add_child(MeshInstance3D(mesh=sphere, material=material, position=((i - 1.5) * 2.4, 0.2, 0)))
83
84 hud = self.add_child(CanvasLayer())
85 self.status = hud.add_child(Label("Sun sweep: running", anchor=Anchor.TOP_LEFT, inset=12, font_size=24))
86 hud.add_child(Label("Space / click / tap: pause the sweep", anchor=Anchor.BOTTOM_LEFT, inset=12))
87
88 def place_sun(self):
89 # The arc climbs from the horizon ahead, passes near the zenith and sets behind the camera.
90 towards_sun = Vec3(0.12, math.sin(self.phase), -math.cos(self.phase))
91 self.sun.direction = -towards_sun
92
93 @on_input("pause")
94 def toggle_pause(self, _event):
95 self.paused = not self.paused
96 self.status.text = f"Sun sweep: {'paused' if self.paused else 'running'}"
97
98 def on_update(self, dt):
99 if not self.paused:
100 self.phase += SWEEP_SPEED * dt
101 self.place_sun()
102
103
104if __name__ == "__main__":
105 App(title="Dynamic Sky", width=1280, height=720).run(DynamicSkyScene())