IBL¶
Image-based lighting on metallic spheres.
▶ Run in browserTags: 3d
Image-based lighting (IBL) lights a surface from the whole sky rather than from
a handful of lights. The sky is a cubemap, and the renderer turns it into a
blurred irradiance map for diffuse light, a prefiltered specular map with one
mip per roughness level for reflections, and a BRDF lookup table. You never
build those yourself: a WorldEnvironment with a sky does it, and every
standard Material in the scene picks it up.
WorldEnvironment(sky_colour_top=(0.15, 0.22, 0.35), sky_colour_bottom=(0.6, 0.5, 0.4))
A colour sky (sky_mode="colour", the default) is a vertical gradient from
sky_colour_top to sky_colour_bottom; sky_mode="procedural" gives a
sun-driven sky and environment_map takes your own cubemap. How much of it
reaches shaded surfaces is ambient_light_energy.
Two rows of spheres show what the material does with that light. The top row is
gold metal (metallic=1.0) with roughness rising from 0.15 on the left to
0.95 on the right: the tight highlight and crisp sky reflection spread into a soft sheen.
The bottom row is red with roughness=0.25 and metallic rising from 0.0
to 1.0: a plastic that reflects a little of the sky becomes a metal tinted by
it. A DirectionalLight3D adds direct light and the highlight on top.
The OrbitCamera3D circles the spheres slowly on its own through
camera.orbit(), and takes its own input as well.
Controls: Drag / right stick - Orbit the camera Scroll / pinch / triggers - Zoom
Source¶
1# /// simvx
2# web = { width = 1280, height = 720, reason = "Larger browser canvas; skybox + IBL run cross-backend." }
3# ///
4"""IBL: Image-based lighting on metallic spheres.
5
6Image-based lighting (IBL) lights a surface from the whole sky rather than from
7a handful of lights. The sky is a cubemap, and the renderer turns it into a
8blurred irradiance map for diffuse light, a prefiltered specular map with one
9mip per roughness level for reflections, and a BRDF lookup table. You never
10build those yourself: a ``WorldEnvironment`` with a sky does it, and every
11standard ``Material`` in the scene picks it up.
12
13 WorldEnvironment(sky_colour_top=(0.15, 0.22, 0.35), sky_colour_bottom=(0.6, 0.5, 0.4))
14
15A colour sky (``sky_mode="colour"``, the default) is a vertical gradient from
16``sky_colour_top`` to ``sky_colour_bottom``; ``sky_mode="procedural"`` gives a
17sun-driven sky and ``environment_map`` takes your own cubemap. How much of it
18reaches shaded surfaces is ``ambient_light_energy``.
19
20Two rows of spheres show what the material does with that light. The top row is
21gold metal (``metallic=1.0``) with ``roughness`` rising from 0.15 on the left to
220.95 on the right: the tight highlight and crisp sky reflection spread into a soft sheen.
23The bottom row is red with ``roughness=0.25`` and ``metallic`` rising from 0.0
24to 1.0: a plastic that reflects a little of the sky becomes a metal tinted by
25it. A ``DirectionalLight3D`` adds direct light and the highlight on top.
26
27The ``OrbitCamera3D`` circles the spheres slowly on its own through
28``camera.orbit()``, and takes its own input as well.
29
30Controls:
31 Drag / right stick - Orbit the camera
32 Scroll / pinch / triggers - Zoom
33"""
34
35import math
36
37from simvx.core import (
38 Anchor,
39 CanvasLayer,
40 DirectionalLight3D,
41 Label,
42 Material,
43 Mesh,
44 MeshInstance3D,
45 Node,
46 OrbitCamera3D,
47 WorldEnvironment,
48)
49from simvx.graphics import App
50
51SPHERE_COUNT = 7
52SPACING = 2.5
53ORBIT_SPEED = 0.15 # radians per second
54
55
56class IBLScene(Node):
57 def on_ready(self):
58 # A deep blue zenith over a warm horizon: the two colours the spheres reflect.
59 self.add_child(WorldEnvironment(sky_colour_top=(0.15, 0.22, 0.35), sky_colour_bottom=(0.6, 0.5, 0.4)))
60 self.camera = self.add_child(
61 OrbitCamera3D(
62 pivot=(0, 3, 0),
63 distance=14.5,
64 yaw=0.0,
65 pitch=math.radians(-14),
66 fov=55,
67 # Keep the player above the ground plane and within sight of the rows.
68 max_pitch=math.radians(-3),
69 min_distance=6,
70 max_distance=30,
71 )
72 )
73 self.add_child(DirectionalLight3D(colour=(1.0, 0.98, 0.92), intensity=1.0, direction=(-5, -10, -8)))
74
75 # A finely tessellated sphere keeps the tight highlight of a smooth metal from
76 # jumping between faces as the camera moves.
77 sphere = Mesh.sphere(0.9, rings=48, segments=64)
78 for i in range(SPHERE_COUNT):
79 t = i / (SPHERE_COUNT - 1)
80 x = (i - SPHERE_COUNT // 2) * SPACING
81 gold = Material(colour=(0.9, 0.6, 0.2), metallic=1.0, roughness=0.15 + 0.8 * t)
82 red = Material(colour=(0.8, 0.1, 0.1), metallic=t, roughness=0.25)
83 self.add_child(MeshInstance3D(mesh=sphere, material=gold, position=(x, 4.5, 0)))
84 self.add_child(MeshInstance3D(mesh=sphere, material=red, position=(x, 1.0, 0)))
85
86 ground = Material(colour=(0.15, 0.15, 0.18), roughness=0.9)
87 self.add_child(MeshInstance3D(mesh=Mesh.plane(25.0), material=ground))
88
89 hud = self.add_child(CanvasLayer())
90 captions = "Top: gold, metallic 1.0, roughness 0.15 -> 0.95\nBottom: red, roughness 0.25, metallic 0.0 -> 1.0"
91 hud.add_child(Label(captions, anchor=Anchor.TOP_LEFT, inset=10, font_size=18))
92
93 # Example help overlay: not part of the technique shown.
94 hud.add_child(Label("Drag: orbit Scroll or pinch: zoom", anchor=Anchor.BOTTOM_LEFT, inset=10, font_size=14))
95
96 def on_update(self, dt):
97 self.camera.orbit(ORBIT_SPEED * dt, 0.0)
98
99
100if __name__ == "__main__":
101 App(title="IBL", width=1280, height=720).run(IBLScene())