API Reference · Scene and camera
Scene
public sealed class Scene : NodeThe scene: a root transform, its nodes, its lights and a background colour.
Everything here lives on the UI thread. Scene.Version ticks on every change, and the control uses that to decide when to hand the renderer a fresh snapshot — so a static scene costs nothing per frame and an animated one costs exactly one rebuild per mutation batch.
Properties
| Member | Description |
|---|---|
| The animators playing over this scene.
A plain list rather than one of the collections that invalidate on change: an animator writes to nodes, and those writes invalidate the scene by themselves. |
| What the viewport is cleared to before anything is drawn. |
| How much of the blurred bright pass is added back. Zero — the default — switches bloom off entirely, and is the only value at which none of the work is done. Around 0.3 is a glow you notice without naming; 1 is a bright haze; above 1 the bloom clips to white and spreads, which is occasionally what a signage or an emergency strip wants. |
| How far the glow spreads, as a standard deviation in device pixels. 16 by default. In pixels rather than in world units, because that is what it is: a lens effect belongs to the picture, not to the scene. A radius in world units would make a lamp's glow shrink as the camera backed away, which is the opposite of what happens. |
| How bright a pixel has to be before it blooms, from 0 for everything to 1 for only pure white. 0.75 by default. Below The threshold is read against the finished picture rather than against the scene's radiance, and that is the one thing worth knowing before tuning it. Bloom here is a compositing step: the frame is drawn, tone-mapped and gamma-encoded first, and what blooms is what came out bright on screen. Doing it properly means blurring the linear image before the tone curve, which needs a second render target on four backends; this needs none and is what almost every real-time bloom did for a decade. What it costs is that the tone curve has already compressed the highlights, so a lamp a hundred times over range and one twice over range both arrive near white and bloom about the same. What it buys is a lamp that reads as lit rather than as a bright patch painted on, which is the whole point. All of that describes the default picture. With |
| Screen-space contact shading from opaque camera-visible meshes. Off by default. Authored AO and contact visibility combine by minimum, so they do not multiply-darken cavities. Transparent and alpha-cutout surfaces do not write this depth pass. |
| Maximum contact distance in world units. Default 0.4; choose for the model's scale. |
| Contact shading strength from 0 (disabled, no depth pass) to 2. Default 1. |
| How far outside the camera's view a node may be and still be submitted, in world units. Zero by default. The escape hatch for geometry that is drawn somewhere its bounds do not describe — a vertex shader that displaces, a sprite grown by a property the bounds were computed before. Nothing in this library needs it, which is why it is zero; it is here so that a consumer who finds an edge does not have to turn the whole feature off to get past it. |
| Replaces mesh shading with a material or lighting diagnostic. Data views bypass exposure, fog and tone mapping; light views use exposure and tone mapping. Sprites and lines are unchanged. The interactive CPU fallback samples maps per vertex; use OfflineRenderer.RenderShaded for per-pixel material inspection. None restores ordinary shading. |
| The ambient hemisphere. Without it metals have nothing to reflect; see |
| The ambient light everything sits in: a sky and ground colour, or an image to reflect. Never null — assigning |
| Multiplies the lit colour before the tone map. One by default. The one control that moves an entire scene's brightness without touching a light, a material or an environment — which is what makes it the right knob for "this is too dark" and the wrong one for "this lamp is too dim". Below one darkens, above one brightens and pushes more of the picture into the shoulder of the tone curve, where highlights compress instead of clipping. |
| The colour distant geometry fades into, in linear RGB like every other colour on a scene. Has no effect until In the default pipeline fog is mixed in after the tone map, so this colour is neither exposed nor tone mapped: it appears as its own sRGB encoding whatever With |
| How far from the camera fog is complete. At or below |
| How far from the camera fog begins. Nothing nearer than this is touched. |
| Whether geometry the camera cannot see is skipped rather than submitted. On by default. A draw call costs between two and ten thousand triangles on the renderers here — measured, on both the Metal and the OpenGL paths — so submitting a room that is behind the camera is the most expensive thing a scene can do. This tests each node's bounds against the camera's six planes and leaves out what cannot appear. It changes what is submitted and never what is drawn. Shadows are unaffected: the depth pass takes its casters from the scene rather than from the camera's draw list, so something behind the camera still casts into the view. And a node is skipped only when its whole bounding box is outside one plane, which is conservative in the direction that matters — a little extra submitted costs a draw call, a little too much skipped is a hole in the picture. Turn it off to compare, or for a scene whose geometry is drawn somewhere other than where its bounds say it is. |
| Linear luminance threshold for bloom while |
| The key light: the first This is how the scene was lit before there could be more than one, and it still works — assigning replaces the first directional light instead of appending. Scenes wanting a second light, or a Reading is a read: it never throws, never adds a light, and never ticks |
| Every light in the scene. A new scene starts with one Lights are plain mutable objects the graph cannot observe, so call |
| Renders and blends scene colour in a linear floating-point target, applies bloom, then exposure, white balance and tone mapping. False retains the inexpensive compatibility pipeline. Material diagnostic views bypass this pipeline. |
| Maximum simultaneous shadowed lights, from zero to two. One by default; zero draws no shadows. |
| How far towards the light a surface is pushed before it is compared against the map, as a fraction of the map's own depth range. 0.0015 by default. This number exists because a shadow map is a depth taken at one resolution being asked a question at another, and the two disagree on every surface that is not square-on to the light. Too small and a lit surface shadows itself in bands — acne, and it looks like moiré crawling as the camera moves. Too large and a shadow detaches from whatever is casting it, so a figure floats above its own shadow. There is no value that is right for every scene, because the error scales with how oblique the surface is and how much world the map covers. The default is tuned for a room-sized scene at 2048. A scene that is kilometres across wants more; a scene of one small object wants less. Most of the slope-dependent part is already handled — the comparison also offsets along the surface normal by a texel's worth of world, which is what removes the acne a constant alone cannot. |
| The subtree the shadow map is built from, or null — the default — for the whole scene. What this is for. One map has one frustum, and the frustum has to enclose everything that casts. So the map's texel size is set by the largest thing casting anywhere in the scene, however far it is from what the camera can see: a corridor thirty-eight metres long spreads a 2048 map over thirty-eight metres, and the room the viewer is standing in gets the few texels that fall in it. Naming a subtree here shrinks the frustum to that subtree, which shrinks the texels with it, and takes everything outside it out of the depth pass as well. Why a node rather than a flag on each mesh. The node does not have to be in this scene's graph, and nothing enforces that it is — a node that is not reached during the walk simply contributes nothing, which is the same outcome as an empty subtree and reads the same way from a picture: no shadows. Set it to null to go back to the whole scene. It scopes the casters and not the receivers. Everything visible still receives, because the depth range is fitted to what a shadow could land on rather than to what casts — see |
| The highest-priority selected casting light, or null when shadows are disabled. |
| Selected casting lights, ordered by priority and then scene order. A backend with fewer available shadow samplers reports its fallback through RenderInfo. |
| The square edge of the shadow map, in pixels. 2048 by default. Zero or less switches shadows off as surely as no light asking for them, though Clamped to 64 at the bottom and 8192 at the top. The bottom is where a map stops describing anything — a room at 32 pixels across is four or five texels per wall — and the top is the largest square every backend here can be relied on to allocate. Nothing rounds it to a power of two; the backends allocate what is asked for. What it costs is memory and one extra pass over the casting geometry per frame. 2048 is 16 MB of depth on a GPU and the same in managed memory on the software renderer, which is why it is a property rather than a constant: a scene showing one object on a turntable wants 1024 and a corridor wants more. |
| How dark a shadowed surface goes, from 0 for no shadow at all to 1 for the light fully blocked. One by default. Below one is not physical and is occasionally what a scene wants anyway: a shadow that keeps some of its light reads as a room with bounce in it, and this is much cheaper than the bounce. It scales the shadow term only, so ambient, the environment and every other light are untouched — a fully shadowed surface is still lit by everything that is not the casting light. |
| Whether shadows are drawn at all. True by default, and the switch an application puts in front of a person. There are three ways to have no shadow and they are three different statements. Off costs nothing rather than nearly nothing: |
| Ticks on every change to the graph. The control watches it to know when to re-snapshot. |
| How much the corners of the picture are darkened, from 0 for none to 1 for black. Zero by default. The cheapest of the three and the one with no physical justification at all: it is a lens artefact that photography spent a century trying to remove and cinema then put back, because a darkened border holds the eye in the middle of the frame. It costs one gradient drawn over the finished image. |
| Linear RGB gains before tone mapping. Applies only while |
Events
| Member | Description |
|---|---|
| Raised on the UI thread whenever anything in the scene changes. |
Methods
| Member | Description |
|---|---|
| Moves every animator in Called by |
| Every |
| Marks the scene changed. Call this after mutating something the graph cannot observe — the fields of a |