Lighting
How a Talos scene is lit in the HD renderer, what each part costs, and what it cannot do yet.
How a Talos scene is lit in the HD renderer, what each part costs, and what it cannot do yet. Four parts make up the "lighting" package:
- Global illumination (GI): bounced light.
- The sky: sun, air, clouds, fog, night.
- The default look: tonemap, exposure, grade.
- Ray tracing: ray-traced sun shadows and reflections.
All four are Beta (docs/FEATURES.md). Field-level reference for the sky and fog is docs/SKY.md; the render settings are in docs/RENDERING.md. GI, ray tracing and the cloud layer are HD only: the standard renderer ignores them (hd-only-ignored).
How to turn things on, off and tune them
Two places:
- The scene (
meta.environment,meta.render): what the world is (sky, fog, GI strength, exposure, look). - The project's quality request (
render.qualityin the project manifest, HD only): how much work the renderer does. A preset (low,medium,high,ultra,custom,auto) plusoverridesfor single knobs. The default is{"preset": "auto", "targetFps": 60}. Auto measures your scene on your GPU at start-up and picks the highest preset that fits the frame budget (80% of 1/targetFps), then steps down or back up as the scene changes. With no GPU timing it falls back to a guess by GPU class. This is windowed play only; a headless run or capture is fixed to High.
"render": { "profile": "hd",
"quality": { "preset": "high", "overrides": { "rayTracing": { "reflections": false, "giProbeUpdates": 2048 } } } }
What the presets set for GI and ray tracing:
| Knob | Low | Medium | High | Ultra |
|---|---|---|---|---|
rayTracing.gi | off | off | on | on |
rayTracing.giProbeUpdates (256..16384) | 1024 | 2048 | 4096 | 8192 |
rayTracing.giCascades (1..4) | 2 | 3 | 3 | 4 |
rayTracing.shadows | off | off | on | on |
rayTracing.reflections | off | off | on | on |
rayTracing.reflectionResolution | half | half | half | full |
rayTracing.reflectionMaxRoughness | 0.4 | 0.5 | 0.5 | 0.7 |
rayTracing.windDeform | off | off | off | on |
rayTracing.giAlways | off | off | off | off |
GI is skipped on a frame with nothing it could light: every surface unlit (a 2D game's sprites, tilemaps and unlit user shaders) and no virtual geometry, splats, particles, water, ground cover, decals or fog volumes. The frame is pixel for pixel the same (GI lights nothing there), 0.6 to 1.2 ms cheaper on an RTX 3080 at 1080p. The first frame with lit content turns GI on for the rest of the run (switching GI rebuilds pipelines, so it never goes back off). rayTracing.giAlways: true keeps GI on from the first frame.
Every ray-traced knob needs a GPU with ray query. Without it the knob falls back to the screen-space path and the engine says quality-unsupported-on-device; if the device or build has no ray tracing at all it says render.hd.rt_unavailable and continues (docs/RUNTIME-CODES.md).
Global illumination
HD's GI is ray traced: a probe cache (radiance probes in giCascades nested levels, giProbeUpdates of them re-traced every frame), a per-pixel final gather from it, and a temporal denoiser. It lights surfaces with sky and sun light that bounces off other surfaces, and occludes sky light in corners. It is on in the High and Ultra presets on a ray-query GPU, and off in Low and Medium (which use the sky's image-based light and ambient occlusion only).
| To | Do |
|---|---|
| Turn it off | render.quality.overrides.rayTracing.gi: false |
| Settle changes faster after a light change | raise rayTracing.giProbeUpdates (more cost) |
| Reach further from the camera | raise rayTracing.giCascades (1..4) |
| Brighten or dim all bounced light | meta.environment.gi.indirectIntensity, 0..16, default 1 (changing it at run time does not restart the GI) |
| Brighten or dim it in one place | a giVolume component: size, indirectIntensity 0..16, mode camera (default) or region, blend soft edge in metres (default 0.5); up to 8 volumes per mode, overlapping volumes multiply |
| Make one light bounce more or less | that light's indirectIntensity |
Details of the controls: RENDERING.md, "GI controls (HD)".
The sky
The HD renderer's physical sky is "kind": "atmosphere" (Rayleigh and Mie scattering, ozone, multiple scattering). The sun is wherever you put it, and the sky, the sun's colour, the image-based light and the aerial perspective all follow. Full fields in SKY.md; the short version:
- Time of day. On an atmosphere sky the
preset(clear-noon,golden-hour,overcast,dusk,night) sets the sun, a cloud layer, the air and a fog together, wherever the scene wrote nothing of its own. Anything the scene writes wins field by field. - Clouds.
sky.clouds:preset(clear,fair,broken,overcast,storm),coverage0..1,altitudeM,thicknessM,density,windSpeedMps,windDirectionRad. They cast shadows on the ground (seerender.hd.cloud_shadow_unavailable). They drift with the simulation clock. Quality knobs:clouds.resolution,clouds.steps,clouds.shadowMapSize. - The air.
sky.atmosphere:preset(earth-clear,hazy,polluted,stylised),haze0..10,visibilityKm0.1..1000 (overrideshaze; 50 clear, 25 typical, 10 hazy, 1 fog),groundAlbedo0..1 (default 0.3), andaerialScale0..100 (default 1 is physical; larger values give a small world the layered blue depth of a big one, without changing the sky itself). - Fog.
meta.environment.fogwith apreset(light-haze,valley-mist,morning-mist,dense,aerial) or avisibilityM.aerial(20 km visibility, thin within about 250 m of height) is the depth cue for a 0.3 to 1 km scene, and the one to use under a panorama sky (see Known limits). A barefog: {}is 0.02 per metre, which is thick. - Night. With the sun below about -4 degrees on an atmosphere sky, HD adds a moon light (the directional light, opposite the sun, 35 degrees up) and a dark blue dome so the image-based light and shaded sides are not black. Both fade in from -4 to -10 degrees. Auto exposure lifts a night frame at most 2 stops; pin
exposurefor a darker night.
The default look
A scene that writes nothing gets:
- Tonemap
agx-punchyin both renderers (meta.render.tonemap; others:agx,aces,neutral,none, HD'sunreal-filmic). - Auto exposure in HD (
exposureMode: "auto", metered every frame), unless the scene setsexposureor the retro style.exposurethen shifts the metered result bylog2(exposure)stops. For a fixed exposure write"exposureMode": "manual"(withexposure, default 1). - No grade.
meta.render.lookabsent draws no grade, the same as{ "preset": "natural" }.
To grade, name a look and tune it:
"render": { "look": { "preset": "cinematic", "contrast": 1.1, "temperatureK": 6000, "shadowsLift": -0.3 } }
Presets natural, cinematic, bright-day, overcast, night, stylised; overrides contrast 0.5..2, saturation 0..2, temperatureK 2000..15000, tint -1..1, shadowsLift -1..1, vignette 0..0.9 (HD). See RENDERING.md, "Scene look".
Ray tracing
Ray-traced sun shadows replace the shadow cascades, and ray-traced reflections replace the screen-space ones. Both need a GPU with ray query. The same ray-tracing core is what GI traces against, so a mesh that is not in it casts and receives neither.
- On or off:
rayTracing.shadowsandrayTracing.reflections(table above). Both are on in High and Ultra on a ray-query GPU and off in Low and Medium. To keep them off in a project that runs High, override them tofalse. - Size floor. The HD setting
rt_min_radius_mleaves instances smaller than that bounding radius (ferns, weeds, debris) out of ray tracing. The default is 0 (off: everything is traced). It is an engine setting, not a scene or project field yet; the capture tool sets it from its manifest. - Wind deformation.
rayTracing.windDeform(Ultra only by default) makes swaying foliage trace the exact deformed shape, about 0.7 ms in a dense forest (from the knob's own description).
What it costs
RTX 5080, 1920x1080, High (all ray-traced effects on), median GPU time per frame spent in the GI and RT passes, measured 16 Oct 2026 on the gate's reference scenes (raw numbers: the per-pass budgets in scripts/gate/perf/gpu-cells.json, added with the gate's advisory per-pass check). Provisional: the gate does not enforce these yet, and they will move as GI changes.
| Scene | GI (probe select, trace, blend, gather, denoise) | RT shadows and reflections | Total |
|---|---|---|---|
| Open water, UI gallery | 0.14 ms | 0.04 ms | about 0.18 ms |
| Driving course | 0.37 ms | 0.05 ms | about 0.42 ms |
| Alpine terrain | 0.32 ms | 0.11 ms | about 0.43 ms |
| Dense forest (one camera) | 0.53 ms | 0.18 ms | about 0.71 ms |
- Low and Medium run no GI and no RT, so these passes cost nothing there. Ultra has more probe updates, a fourth cascade and full-resolution reflections and has not been measured per pass.
gi-probe-blendis a fixed 0.07 ms in every scene. Everything else scales with foliage and what the camera sees.- RTX 3080: roughly 1.7 times these numbers (an estimate, not yet measured per pass), and more in leaf-heavy frames (a gather over a frame full of leaf cards read 1.9 ms on the 3080 against 0.25 ms for opaque terrain).
- After a camera cut in a leaf view the GI re-converges at 1.4 to 2.2 ms for a short while (llvmpipe and 5080 measurements; not gated).
- Idle cost of having the ray-tracing core built at all: about 0.05 ms and 300 to 420 MiB of GPU memory.
Known limits
- GI over thin foliage is dark. Leaves are handled approximately (the gather sees the front hemisphere only), so forest floors, undergrowth and crowns read darker than they should and lack a translucent glow.
- Probe spacing is coarse, 0.5 m, so thin walls and small gaps can leak or darken a little, and a dim room lit by a small lamp is noisy.
- Far-terrain patches. GI rays hit a coarse terrain proxy that can sit above the true surface in hollows, which gives soft darker patches (about 10%) on distant hills.
- Noise and settling. After a camera cut or a fast light change the GI takes a couple of seconds to settle, and small-source interiors stay blotchy longer.
- Meshes over the ray-tracing binding limit (very large scanned meshes, millions of triangles) are drawn but not traced: no ray-traced shadow, reflection or GI from them, and the log says
render.hd.rt_mesh_too_largeonce per mesh. An automatic simplified copy for such meshes is in progress. Until then use a decimated mesh. - Small plants under
rt_min_radius_mcast no ray-traced sun shadow when the floor is set, because with ray-traced shadows on the cascades are off. Leave the floor at 0 if small plants must shadow. - Captures (splat or probe captures) are not seen by ambient occlusion or GI.
- No stars and no moon disc in the night sky: night has a moon light and a dark dome only.
- No aerial perspective under a panorama (
hdr) sky. It runs only with"kind": "atmosphere". Under a panorama use the fog presetaerialas the depth cue. - The terrain horizon map is off by default. Set
horizon: trueon the terrain component for terrain shadows past the cascades and valleys that darken when the sun sets behind a ridge (RENDERING.md). - Foliage and rough ray-traced reflections: thin foliage pixels skip the reflection ray for cost, so reflections of leaves are not ray traced.
Troubleshooting
Shade looks too dark
- Ambient occlusion stacks on GI under plants and in shadow. Check
ambientOcclusion.enabledin the quality overrides; lower the AO knobs or accept the known foliage limit above. - Raise
meta.environment.gi.indirectIntensity(or place agiVolume) to lift bounced light. - Auto exposure meters a bright sky and leaves shadows low. Shift it with
exposure(stops =log2(exposure)), or setexposureMode: "manual". - Low and Medium have no GI, so shadowed ground gets only the sky's flat light.
- A closed mesh around the camera (a backdrop sphere, a room) blocks ray-traced sun shadows and traps GI inside; in a recording or capture the log says
render.hd.black_frame. Give the mesh a material file with"castShadows": false.
Looks milky or washed out
- Fog too thick: a bare
fog: {}is 0.02 per metre. Write apresetor avisibilityM. - A time-of-day preset brings its own fog (
golden-hourandovercastlight-haze,duskvalley-mist). Write"fog": { "density": 0 }to remove it. - Air too hazy: raise
atmosphere.visibilityKm, lowerhaze, and checkaerialScaleis not set above 1 for a large world. - A tonemap of
noneor a look with lowcontrastalso flattens the image.
Banding in the sky or on distant slopes. This is not a colour-precision problem. Measured on the 5080: output dithering is on, the sky lookup is clean, and the same view with clouds off is a smooth gradient. The bands are the cloud layer's own structure: the deck has a sharp flat base, and its shadows lie flat on far slopes, most visible in a low-sun glow. clouds.steps does not help (32, 64 and 128 steps give the same image). Change clouds.preset or coverage, or set "clouds": { "preset": "clear" } to remove the layer. A softer cloud base is a planned look change.
Codes to look for (docs/RUNTIME-CODES.md, docs/RULE-CODES.md):
| Code | Means |
|---|---|
render.hd.rt_unavailable | no ray tracing on this device or build; the screen-space fallbacks are used |
quality-unsupported-on-device | a ray-tracing knob fell back because the GPU cannot do it |
render.hd.rt_mesh_too_large | a mesh is not traced: no RT shadow, reflection or GI from it |
render.hd.black_frame | a frame came out black, often a closed mesh around the camera |
render.hd.horizon_map_unavailable | device too limited for the terrain horizon map |
render.hd.cloud_shadow_unavailable | clouds draw but cast no shadow on this device |
render.hd.rt-deform-over-budget | swaying instances trace their undeformed shape |
render-clouds-without-atmosphere | sky.clouds is set but the sky is not atmosphere |
hd-only-ignored | a GI, cloud or exposure field was set on the standard renderer |