Skip to content

Lighting & shadows

The six examples the gallery lists under Lighting & shadows. Each block is what the gallery’s code panel shows beside that example, copied from the example itself: the lines that do it rather than the whole file, so it isn’t compiled here the way the guides’ code is. The file it came from is linked under each one.

Sun, point and spot, in lux and lumens, with the shadows each casts.

// Sun: lux, and a width in degrees that decides how soft its shadows are.
OrblitLight(
key: 200,
kind: OrblitLightKind.directional,
intensity: 82000, // lux
direction: Vector3(-0.4, -1, -0.5)..normalize(),
sunAngularRadius: 0.53, // degrees; the real sun's
castShadows: true,
)
// Point: lumens, a position, and a distance it stops mattering past.
OrblitLight(
key: 200,
kind: OrblitLightKind.point,
intensity: 12000, // lumens
position: Vector3(3, 3.4, 3),
falloffRadius: 24, // metres
sourceRadius: 0.5, // how wide the bulb is
)
// Spot: the same, aimed, with the cone it throws.
OrblitLight(
key: 200,
kind: OrblitLightKind.spot,
intensity: 12000,
position: Vector3(3, 3.4, 3),
direction: aim,
innerConeAngle: 25 * pi / 180, // full brightness inside this
outerConeAngle: 45 * pi / 180, // nothing outside it
)
// Watts, metres and degrees belong in orblit_light, which converts them once.

From lights.dart. To open the gallery on it:

Terminal window
ORBLIT_EXAMPLE='lights' flutter run -d macos

The soft shadow a rectangular light casts, and how its edge changes with the size of the panel.

// A rectangle of light, hung above the floor and pointed at it.
//
// `castShadows` is what this example is about. A rectangle is not one of
// Filament's own lights — it is shaded in the surface material against a
// fitted table — so its shadow is a depth map of its own, drawn once from
// where the panel stands and compared against by every surface it reaches.
OrblitLight(
key: 1,
kind: OrblitLightKind.area,
// Lumens off a surface rather than out of a point, so a wider panel
// spreads the same light instead of adding more of it.
intensity: 900000,
position: Vector3(0, 5, 0),
direction: Vector3(0, -1, 0),
// Which way the width runs. A strip on its side is a different light.
tangent: Vector3(1, 0, 0),
width: 2,
height: 2,
falloffRadius: 30,
castShadows: true,
)
// One rectangle casts. A scene has one key light and the rest are fill, and
// giving every panel a map would cost a scene render each to shadow lights
// whose job is to not be noticed. A second one asking is reported and lit
// without a shadow rather than dropped.

From panel_shadow.dart. To open the gallery on it:

Terminal window
ORBLIT_EXAMPLE='panel shadows' flutter run -d macos

Filament’s own shadows: sun, spot and point, with every setting — map size, cascades and splits, bias, distance, contact shadows, and the four kinds of edge.

// Shadow settings belong to the pipeline, not to one light: every light
// that casts reads them. Filament keeps them on each light internally, and
// the renderer copies them onto every one whenever they change.
OrblitScene(
pipeline: OrblitPipeline(
shadows: OrblitShadows(
// Sharp (PCF), Soft (DPCF), Area (PCSS) or Variance (VSM).
kind: OrblitShadowKind.area,
mapSize: 2048,
// A sun's map split into cascades running away from the camera. The
// splits are fractions of the distance; null lets lambda place them.
cascades: 3,
splits: [0.08, 0.3],
distance: 60,
constantBias: 0.001,
normalBias: 1.0,
// Locked to the world, so an edge does not crawl as the camera turns.
stable: true,
// A short march through the depth buffer, for what is too small for
// the map: a stick on the floor, a foot on the ground.
contact: true,
contactDistance: 0.3,
// For Area: how wide, and how fast it widens with distance.
softness: 1.0,
softnessFalloff: 1.0,
// Only Variance reads these.
variance: OrblitVarianceShadows(blur: 4, lightBleedReduction: 0.3),
),
),
lights: [
OrblitLight(
key: 1,
kind: OrblitLightKind.spot,
// The light's real size: what an Area edge is made from.
sourceRadius: 0.4,
castShadows: true,
...
),
],
...
)
// Not reachable: caching a shadow map between frames. Filament redraws
// every map every frame and has no API to hold one still.

From shadows.dart. To open the gallery on it:

Terminal window
ORBLIT_EXAMPLE='shadows' flutter run -d macos

A chrome box reflecting the room it is in, from a cubemap the scene captured of itself.

// The scene photographs itself from a point inside, and is lit by that
// instead of by the environment. Captured once and kept: six renders of
// the whole scene is not a per-frame cost, so bumping `version` is how a
// host says the room has changed.
OrblitScene(
probes: [
OrblitProbe(
key: 100,
position: Vector3(0, -0.4, 0), // head height, not the floor
radius: 14, // how far its influence reaches
resolution: 256,
// Everything but the reflective things. A probe captured from inside
// a mirror photographs the mirror, and the mirror then reflects a
// smaller copy of itself.
layers: 1 << 0,
),
],
// ...
)

From probes.dart. To open the gallery on it:

Terminal window
ORBLIT_EXAMPLE='reflection probes' flutter run -d macos

One bounce, taken from the picture already drawn: coloured walls tinting a white box between them.

// One effect pass over the finished picture. It reads the colour and the
// depth of the same target, so the graph names that target once.
OrblitRenderGraph(
targets: const [OrblitTarget(name: 'frame')],
passes: [
const OrblitPass(name: 'world', into: 'frame'),
OrblitPass(
name: 'bounce',
kind: OrblitPassKind.effect,
effect: OrblitEffect.bounce,
reads: const ['frame'],
// How far it looks, how much comes back, how solid the depth
// buffer's surfaces are, and how many directions each pixel fans
// along. Nought means the renderer's own default.
plane: [3.0, 4.0, 0, 0],
),
],
)

From bounce.dart. To open the gallery on it:

Terminal window
ORBLIT_EXAMPLE='bounced light' flutter run -d macos

Probes standing in the room, holding the light that reaches them, so indirect light survives the camera looking away.

OrblitScene(
field: OrblitField(
enabled: true,
origin: Vector3(-4, -2.6, -4), // where the corner probe stands
spacing: Vector3(2, 2, 2), // metres between probes
counts: Vector3(5, 4, 5), // how many along each axis
from: 'frame', // the target the probes read
intensity: 1.6,
retention: 0.94, // how much survives each frame
),
// The probes read the picture the scene drew, so it has to go into a
// target first and then be put on the screen.
graph: OrblitRenderGraph(
targets: const [OrblitTarget(name: 'frame')],
passes: const [
OrblitPass(name: 'world', into: 'frame'),
OrblitPass(
name: 'present',
kind: OrblitPassKind.effect,
effect: OrblitEffect.copy,
reads: ['frame'],
),
],
),
// ...
)

From field.dart. To open the gallery on it:

Terminal window
ORBLIT_EXAMPLE='irradiance field' flutter run -d macos