Screen-space reflections¶
a glossy floor mirroring the scene above it.
▶ Run in browserTags: 3d ssr reflections screen-space pbr
Screen-space reflections (design D7) trace the reflected view ray through the depth buffer + thin G-buffer and feed the result into the pluggable indirect-specular ambient hook (design D8), so the glossy floor mirrors the coloured pillars and floating shapes standing on it without any cubemap or probe. The reflection is Hi-Z traced at half resolution and roughness-aware: the mirror floor gives a sharp reflection, the frosted panel a blurred one, and reflections fade as they approach the screen edge (the on-screen-only limit of SSR). A ray that leaves the screen falls back to the flat/IBL ambient, so nothing goes black.
Usage: uv run python examples/features/3d/ssr.py
Controls: Space - Toggle SSR on/off Escape - Quit
Source¶
1"""Screen-space reflections: a glossy floor mirroring the scene above it.
2
3Screen-space reflections (design D7) trace the reflected view ray through the
4depth buffer + thin G-buffer and feed the result into the pluggable
5indirect-specular ambient hook (design D8), so the glossy floor mirrors the
6coloured pillars and floating shapes standing on it without any cubemap or probe.
7The reflection is Hi-Z traced at half resolution and roughness-aware: the mirror
8floor gives a sharp reflection, the frosted panel a blurred one, and reflections
9fade as they approach the screen edge (the on-screen-only limit of SSR). A ray
10that leaves the screen falls back to the flat/IBL ambient, so nothing goes black.
11
12# /// simvx
13# tags = ["3d", "ssr", "reflections", "screen-space", "pbr"]
14# screenshot_frame = 40
15# ///
16
17Usage:
18 uv run python examples/features/3d/ssr.py
19
20Controls:
21 Space - Toggle SSR on/off
22 Escape - Quit
23"""
24
25import math
26
27from simvx.core import (
28 Camera3D,
29 DirectionalLight3D,
30 Input,
31 InputMap,
32 Key,
33 Material,
34 Mesh,
35 MeshInstance3D,
36 Node3D,
37 PointLight3D,
38 Quat,
39 Text2D,
40 Vec3,
41 WorldEnvironment,
42)
43from simvx.graphics import App
44
45WIDTH, HEIGHT = 1280, 720
46
47
48class SSRScene(Node3D):
49 def __init__(self, **kwargs):
50 super().__init__(name="SSRDemo", **kwargs)
51
52 # A low, near-grazing camera: reflections stretch across the floor toward
53 # the viewer (the classic wet-street SSR look) and the Fresnel term boosts
54 # the reflection at the glancing angle.
55 self.camera = self.add_child(Camera3D(name="Camera", fov=52, near=0.1, far=120.0))
56 self.camera.position = Vec3(0.0, 1.7, 10.5)
57 self.camera.look_at(Vec3(0.0, 1.5, -2.0))
58
59 sun = self.add_child(DirectionalLight3D(name="Sun"))
60 sun.colour = (1.0, 0.96, 0.9)
61 sun.intensity = 2.4
62 sun.rotation = Quat.from_euler(math.radians(-55), math.radians(35), 0)
63
64 fill = self.add_child(PointLight3D(name="Fill", position=Vec3(-5, 5, 5)))
65 fill.colour = (0.5, 0.6, 1.0)
66 fill.intensity = 0.8
67 fill.range = 30.0
68
69 # Semi-metallic, near-mirror floor: low roughness so SSR reads sharply,
70 # a touch of metalness so the reflection is visible across viewing angles.
71 floor_mat = Material(colour=(0.5, 0.52, 0.58), metallic=0.9, roughness=0.06)
72 self.add_child(
73 MeshInstance3D(
74 name="Floor",
75 mesh=Mesh.cube(1.0),
76 material=floor_mat,
77 position=Vec3(0, -0.1, 0),
78 scale=Vec3(24, 0.2, 24),
79 )
80 )
81
82 # Colourful pillars + shapes standing on the floor, so their reflection is
83 # unmistakable in the mirror below.
84 palette = [
85 (0.90, 0.25, 0.22),
86 (0.20, 0.70, 0.35),
87 (0.25, 0.45, 0.95),
88 (0.95, 0.78, 0.18),
89 (0.80, 0.30, 0.80),
90 ]
91 for i, x in enumerate([-6, -3, 0, 3, 6]):
92 mat = Material(colour=palette[i], metallic=0.1, roughness=0.4)
93 self.add_child(
94 MeshInstance3D(
95 name=f"Pillar{i}",
96 mesh=Mesh.cube(1.0),
97 material=mat,
98 position=Vec3(x, 1.6, -3.0),
99 scale=Vec3(1.1, 3.2, 1.1),
100 )
101 )
102
103 # Floating shapes above the floor centre.
104 self.add_child(
105 MeshInstance3D(
106 name="Sphere",
107 mesh=Mesh.sphere(1.1, rings=24, segments=32),
108 material=Material(colour=(0.95, 0.55, 0.15), metallic=0.2, roughness=0.25),
109 position=Vec3(-2.2, 1.3, 1.5),
110 )
111 )
112 self.add_child(
113 MeshInstance3D(
114 name="Cube",
115 mesh=Mesh.cube(1.6),
116 material=Material(colour=(0.15, 0.85, 0.85), metallic=0.3, roughness=0.2),
117 position=Vec3(2.4, 1.1, 2.0),
118 rotation=Quat.from_euler(0, math.radians(30), 0),
119 )
120 )
121 # A frosted (rougher) panel to show roughness-blurred reflections.
122 self.add_child(
123 MeshInstance3D(
124 name="Panel",
125 mesh=Mesh.cube(1.0),
126 material=Material(colour=(0.85, 0.85, 0.9), metallic=0.1, roughness=0.35),
127 position=Vec3(0.0, 2.2, -5.4),
128 scale=Vec3(6.0, 3.2, 0.3),
129 )
130 )
131
132 self._ssr_on = True
133 self._cooldown = 0.0
134 self._env = self.add_child(WorldEnvironment(name="Env"))
135 self._env.ambient_light_colour = (0.12, 0.13, 0.16)
136 self._env.ambient_light_energy = 0.5
137 self._env.ssr_enabled = True
138 self._env.ssr_intensity = 1.0
139 self._env.ssr_max_distance = 40.0
140
141 self._title = self.add_child(Text2D(text="SCREEN-SPACE REFLECTIONS", position=(10, 8), font_scale=1.5))
142 self._status = self.add_child(Text2D(text="SSR: ON", position=(10, 40), font_scale=1.3))
143 self.add_child(Text2D(text="SPACE:Toggle SSR ESC:Quit", position=(10, 690), font_scale=1.1))
144
145 def on_ready(self):
146 InputMap.add_action("toggle_ssr", [Key.SPACE])
147 InputMap.add_action("quit", [Key.ESCAPE])
148
149 def on_update(self, dt: float):
150 self._cooldown = max(0.0, self._cooldown - dt)
151 if Input.is_action_just_pressed("toggle_ssr") and self._cooldown <= 0.0:
152 self._ssr_on = not self._ssr_on
153 self._cooldown = 0.3
154 self._env.ssr_enabled = self._ssr_on
155 self._status.text = f"SSR: {'ON' if self._ssr_on else 'OFF'}"
156 if Input.is_action_just_pressed("quit"):
157 self.app.quit()
158
159
160def main():
161 scene = SSRScene()
162 app = App(title="SimVX SSR Demo", width=WIDTH, height=HEIGHT, physics_fps=60)
163 app.run(scene)
164
165
166if __name__ == "__main__":
167 main()