Screen-space reflections

a glossy floor mirroring the scene above it.

▶ Run in browser

Tags: 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()