WorldEnvironment

WorldEnvironment is where you configure post-processing, sky, fog, and global rendering settings. Add one as a child of any node in your scene tree and set its properties: the renderer syncs them every frame. Never reach into the renderer directly.

Basic usage

from simvx.core import Node, WorldEnvironment, Camera3D

class Game(Node):
    def on_ready(self):
        env = self.add_child(WorldEnvironment())
        env.bloom_enabled = True
        env.bloom_threshold = 0.8
        env.ssao_enabled = True
        env.tonemap_mode = "aces"
        env.tonemap_exposure = 1.2

        self.add_child(Camera3D(position=(0, 3, 8)))

Only one WorldEnvironment should be active per scene; the renderer picks the first one it finds during scene submission.

Property groups

Every setting is a Property: visible in the inspector and saved with the scene.

Group

Properties

Ambient

ambient_light_colour, ambient_light_energy, ambient_light_mode

Sky

sky_mode, sky_colour, sky_texture, environment_map

Fog

fog_enabled, fog_colour, fog_density, fog_start, fog_end, fog_mode, fog_height, fog_height_density

Volumetric Fog

volumetric_fog_enabled, volumetric_fog_density, volumetric_fog_anisotropy, volumetric_fog_length, volumetric_fog_gi_inject, volumetric_fog_temporal_reprojection

Tonemap

tonemap_mode ("linear", "reinhard", "aces", …), tonemap_exposure, tonemap_white

Bloom

bloom_enabled, bloom_threshold, bloom_intensity, bloom_soft_knee

SSAO

ssao_enabled, ssao_radius, ssao_bias, ssao_intensity

Depth of Field

dof_enabled, dof_focus_distance, dof_focus_range, dof_max_coc

Motion Blur

motion_blur_enabled, motion_blur_intensity, motion_blur_samples

Film Effects

film_grain_enabled/intensity, vignette_enabled/intensity/smoothness, chromatic_aberration_enabled/intensity, crt_enabled/intensity, pixelate_enabled/size, blur_enabled/radius

Anti-Aliasing

fxaa_enabled, taa_enabled

Colour Grading

colour_grading_enabled, lut_enabled, lut_tex_id

Shadows

shadow_cascade_count, shadow_debug_cascades

Quality

quality_tier ("auto", "low", "medium", "high", "ultra", "custom"), render_scale, probe_blend_count, probe_face_size, shadow_caster_count, scene_feedback_max_depth

Quality tiers

quality_tier is one string dial resolved through the tier table in simvx.core.graphics_quality, so a tier means the same thing on the Vulkan and WebGPU backends. Two of its values are selectors rather than tiers. "auto" (the default) consults no table: the backend decides, and the per-dial properties below apply as they are. "custom" is the explicit pass-through, where the dials are entirely yours. "high" is pinned to the engine defaults, so it renders exactly like "auto" on desktop.

The table sets four dials:

Dial

low

medium

high

ultra

render_scale

0.5

1.0

1.0

1.0

probe_blend_count

0

2

2

8

probe_face_size

64

64

128

128

shadow_caster_count

0

1

1

4

A tier never overrules a decision you made: a dial you explicitly moved off its default wins over the tier value, and "custom" (or "auto") keeps the table out of it altogether. That is the manual override; there is no separate opt-out.

  • render_scale sizes the whole main-view HDR chain; the tonemap draw upscales to the window. 1.0 is native and byte-identical to the unscaled path.

  • probe_blend_count is how many reflection probes blend per fragment. 0 turns probe capture and blending off entirely, and ambient falls back to the sky IBL or the flat fill.

  • probe_face_size is the per-face capture resolution of those probes.

  • shadow_caster_count is how many point lights, and how many spot lights, may cast a shadow at once (Vulkan only: the web forward shader samples only the directional cascades). A shadow map is redrawn only when its light or a casting mesh changes, so a still scene costs almost nothing per frame however many casters it has. Lights past the budget still light the scene without a shadow; shadow_priority on PointLight3D and SpotLight3D (higher wins), then each light’s importance to the camera, decides which keep one.

env.quality_tier = "ultra"          # four positional casters, 8-probe blend
env.shadow_caster_count = 2         # explicit: wins over the tier

2D and post-processing

When post-processing is on, world-space 2D (sprites, shapes, particles, text) is composited inside the HDR pass before tonemapping, so it gets the same exposure, tonemap and bloom as the 3D scene: a bright 2D shape blooms like an emissive surface. Screen-space 2D (anything on a screen-pinned CanvasLayer) is drawn after tonemapping, at its authored colour.

A node’s or CanvasLayer’s post_processed property overrides that: None (the default) follows where it is drawn, True puts it through the post chain (a HUD element that blooms), False keeps it at its authored colour.

sprite.post_processed = False   # ignore tonemap and bloom for this sprite

See examples/features/3d/world_2d_in_hdr.py.

Effects in a pure-2D game

A 2D game gets post effects by adding a WorldEnvironment and enabling one. Post-processing is opt-in: with no WorldEnvironment, or none of its effects enabled, 2D renders directly at no extra cost. With an effect on, 2D goes through the HDR path with a linear tonemap, so flat art keeps its colour while bright shapes glow.

env = self.add_child(WorldEnvironment())
env.bloom_enabled = True
env.bloom_threshold = 0.6       # below 1.0, ordinary bright sprites glow
env.crt_enabled = True          # scanlines (crt_intensity)
env.pixelate_enabled = True     # mosaic (pixelate_size, in pixels)

Screen-space effects (bloom, vignette, film grain, chromatic aberration, colour grading, FXAA, CRT, pixelate, blur) apply to 2D; depth-based ones (SSAO, depth of field, motion blur, fog) do not. Both work on desktop and web. See examples/features/2d/bloom.py and examples/features/2d/screen_effects.py.

Custom post-processing

PostProcessEffect wraps a fragment shader as a fullscreen pass:

from simvx.core import PostProcessEffect

grayscale = PostProcessEffect(
    shader_code="""
        void main() {
            vec2 uv = gl_FragCoord.xy / u_resolution;
            vec3 c = texture(u_colour_tex, uv).rgb;
            frag_colour = vec4(vec3(dot(c, vec3(0.299, 0.587, 0.114))), 1.0);
        }
    """,
    order=10,
)
grayscale.set_uniform("intensity", 0.5)
env.add_post_process(grayscale)

Standard uniforms wired automatically: u_time (float), u_resolution (vec2), u_colour_tex (sampler2D), u_depth_tex (sampler2D). Effects run in order ascending after the built-in post chain.

API reference

See simvx.core.WorldEnvironment, simvx.core.Environment, and simvx.core.PostProcessEffect.