Skip to content

Looks

A look is a finished setting for a scene’s light. Pick one by name on World, and it sets the following together:

  • An HDRI sky that every model’s material takes light and reflections from.
  • Fog.
  • Exposure.
  • A colour grade.
  • A post-processing stack.

A game that picks no look draws with the daylight rig alone.

import { LookName, World } from "@daniel-zarinski/engine";
export function DuskScene() {
return <World map="meadow" look={LookName.Dusk} />;
}
Look Hour What it looks like
LookName.Noon 10 A clear meadow with the sun high.
LookName.Morning 2.4 A low sun through a light haze.
LookName.Dusk 19.3 A warm sun on the horizon, and a strong glow.
LookName.Overcast 10 Soft light from a grey sky, and close fog.

Each look’s sky is a 1K HDRI from Poly Haven, about 1.5 MB. It lights the scene’s materials but does not replace the drawn sky. The daylight rig keeps the sky and the light in agreement every frame:

  • It clips the image’s own sun, because the rig’s directional light is the sun and casts the shadows. Left in, the image’s sun would light every shadow.
  • It turns the image so the image’s sun stands on the rig’s sun.
  • It dims the image with the hour, as it dims the hemisphere fill.
  • It multiplies the hour’s exposure by the look’s.
  • It dims the fog with the light on the ground, the sun and the fill together. The fog keeps the look’s colour at the look’s hour and at any brighter hour, and darkens as the light falls after it, so it never glows past a scene the night has darkened.

A look opens the World on its hour, where the rig’s sun stands near the height of the image’s sun. Pass startHour to open elsewhere. The day clock still runs, so the light follows the hour from there.

Pass { name, ...values } in place of the name to replace any value of the look. A pass’s settings merge into the look’s, and false drops the pass or the fog:

import { LookName, World } from "@daniel-zarinski/engine";
export function BrightNoonScene() {
return (
<World
map="meadow"
look={{
name: LookName.Noon,
exposure: 1.2,
bloom: { intensity: 1 },
vignette: false,
}}
/>
);
}
Value What it sets
hour The hour the World opens on.
environment file, the .hdr URL; sunU, the column its sun stands in, 0 to 1; intensity, the light it lends every surface at the sun’s peak.
sun The share of the rig’s sun kept: less under cloud.
fill The share of the rig’s hemisphere fill kept beside the sky’s light.
exposure Multiplies the rig’s exposure for the hour.
fog color, near and far in metres, or false.
postProcessing false keeps the look’s light and fog and mounts no stack, so the game can mount its own <PostProcessing>.
bloom Bloom’s props, or false.
ambientOcclusion AmbientOcclusion’s props, or false.
vignette Vignette’s props, or false.
grading hue in radians, and saturation, brightness and contrast from -1 to 1, applied before tone mapping. 0 leaves the frame as it is.

Picking a look opts the game in to post-processing: World mounts the look’s stack, and the devtools show its passes’ sliders. A scene that mounts its own <PostProcessing> keeps it: the look mounts no stack beside it and logs a warning, so the frame is drawn once. Pass postProcessing: false to keep your own stack without the warning.

Roblox sets a scene’s light with fixed settings on its Lighting service, and Unity and Unreal leave the sky, fog and post-processing volume for the creator to tune. Godot gathers them in one Environment resource. A look is closest to Roblox’s: the creator picks a name and never tunes a stack, and an agent starts from a finished look and changes one value at a time.

A scene that draws its own ground, such as a track, has no map to hand World. Mount Lighting instead. It sets the same sky, sun, look and stack as World does, over a point you name rather than the player hero:

import { Lighting, LookName } from "@daniel-zarinski/engine";
import { Vector3 } from "three";
export function TrackLight() {
return (
<Lighting
look={LookName.Morning}
focus={new Vector3(0, 0, -120)}
shadowBox={{ halfWidth: 60, depth: 220 }}
/>
);
}

With no day clock in the world, the sun stands at the look’s hour. shadowBox sizes the sun’s shadow box in metres around focus; the default holds a hero’s surroundings.

To keep a painted sky, pass background: an equirectangular image, and sunU, the column its sun stands in from 0 to 1 across. The image is drawn in place of the rig’s sky, turned so its sun stands on the rig’s. It is only drawn; the look’s HDRI still lights the models.

import { Lighting, LookName } from "@daniel-zarinski/engine";
import dawn from "./sky-dawn.webp?url";
export function DawnLight() {
return (
<Lighting look={LookName.Dusk} background={{ file: dawn, sunU: 0.5 }} />
);
}

Client, except the hour. Headless, World mounts no rig and no stack, but the look’s hour still seeds the day clock, so a simulation opens on the same hour a player sees.