Render scale¶
the 3D scene at half resolution, the UI at native resolution.
▶ Run in browserTags: 3d render-scale quality post-processing
WorldEnvironment.render_scale sizes the whole HDR chain (colour, depth,
SSAO, bloom) at ceil(window * render_scale); the tonemap pass bilinearly
upscales to the window (enable TAA to upgrade the upscale to temporal
upsampling instead, see taau.py). This trades sharpness for fill-rate: the
scene here renders at 0.5 (a quarter of the pixels) while the 2D overlay text
stays crisp because screen-space 2D always draws at native resolution after
the upscale. The thin pillars and the glowing sphere make the softening easy
to see when switching scales at runtime.
Usage: uv run python examples/features/3d/render_scale.py
Controls: 1 / 2 / 3 / 4 - render_scale 0.25 / 0.5 / 0.75 / 1.0 Escape - quit
Source¶
1"""Render scale: the 3D scene at half resolution, the UI at native resolution.
2
3``WorldEnvironment.render_scale`` sizes the whole HDR chain (colour, depth,
4SSAO, bloom) at ``ceil(window * render_scale)``; the tonemap pass bilinearly
5upscales to the window (enable TAA to upgrade the upscale to temporal
6upsampling instead, see taau.py). This trades sharpness for fill-rate: the
7scene here renders at 0.5 (a quarter of the pixels) while the 2D overlay text
8stays crisp because screen-space 2D always draws at native resolution after
9the upscale. The thin pillars and the glowing sphere make the softening easy
10to see when switching scales at runtime.
11
12# /// simvx
13# tags = ["3d", "render-scale", "quality", "post-processing"]
14# screenshot_frame = 30
15# ///
16
17Usage:
18 uv run python examples/features/3d/render_scale.py
19
20Controls:
21 1 / 2 / 3 / 4 - render_scale 0.25 / 0.5 / 0.75 / 1.0
22 Escape - quit
23"""
24
25from simvx.core import (
26 Camera3D,
27 DirectionalLight3D,
28 Input,
29 InputMap,
30 Key,
31 Material,
32 Mesh,
33 MeshInstance3D,
34 Node,
35 Text2D,
36 WorldEnvironment,
37)
38from simvx.graphics import App
39
40WIDTH, HEIGHT = 1280, 720
41
42_SCALES = {"scale_025": 0.25, "scale_050": 0.5, "scale_075": 0.75, "scale_100": 1.0}
43
44
45class RenderScaleScene(Node):
46 def on_ready(self):
47 InputMap.add_action("quit", [Key.ESCAPE])
48 InputMap.add_action("scale_025", [Key.KEY_1])
49 InputMap.add_action("scale_050", [Key.KEY_2])
50 InputMap.add_action("scale_075", [Key.KEY_3])
51 InputMap.add_action("scale_100", [Key.KEY_4])
52
53 # Half-resolution HDR chain; bloom shows the chain itself is scaled.
54 self.env = self.add_child(WorldEnvironment(render_scale=0.5, bloom_enabled=True, bloom_threshold=1.2))
55
56 self.add_child(Camera3D(position=(0, 3.2, 9.0), look_at=(0, 1.2, 0), up=(0, 1, 0)))
57 sun = self.add_child(DirectionalLight3D(intensity=2.2))
58 sun.direction = (-0.5, -1.0, -0.4)
59
60 # Ground slab.
61 self.add_child(
62 MeshInstance3D(
63 mesh=Mesh.cube(1.0),
64 material=Material(colour=(0.55, 0.55, 0.6, 1.0), roughness=0.9),
65 position=(0, -0.25, 0),
66 scale=(16.0, 0.5, 16.0),
67 )
68 )
69 # A fence of thin pillars: high-frequency verticals that visibly soften
70 # below scale 1.0 (the classic render-scale tell).
71 for i in range(-5, 6):
72 self.add_child(
73 MeshInstance3D(
74 mesh=Mesh.cube(0.18),
75 material=Material(colour=(0.85, 0.35, 0.2, 1.0), roughness=0.5),
76 position=(i * 1.1, 1.4, -3.0),
77 scale=(1.0, 15.0, 1.0),
78 )
79 )
80 # Emissive sphere: bloom extraction runs at the internal resolution.
81 self.add_child(
82 MeshInstance3D(
83 mesh=Mesh.sphere(0.8),
84 material=Material(colour=(0.3, 0.7, 1.0, 1.0), emissive_colour=(0.3, 0.6, 1.0, 4.0)),
85 position=(0, 1.3, 0.5),
86 )
87 )
88 self.add_child(
89 MeshInstance3D(
90 mesh=Mesh.cube(1.1),
91 material=Material(colour=(0.35, 0.75, 0.4, 1.0), roughness=0.35),
92 position=(2.8, 0.55, 1.6),
93 )
94 )
95
96 # Native-resolution UI: stays crisp at any render_scale.
97 self.label = self.add_child(
98 Text2D(text="render_scale = 0.5 (keys 1-4)", position=(20, 20), font_scale=1.8)
99 )
100
101 def on_update(self, dt):
102 if Input.is_action_pressed("quit"):
103 self.app.quit()
104 return
105 for action, scale in _SCALES.items():
106 if Input.is_action_just_pressed(action) and self.env.render_scale != scale:
107 self.env.render_scale = scale
108 self.label.text = f"render_scale = {scale} (keys 1-4)"
109
110
111def main():
112 App(width=WIDTH, height=HEIGHT, title="SimVX - Render Scale").run(RenderScaleScene())
113
114
115if __name__ == "__main__":
116 main()