Lighting a 3D scene
The time of day in one line (Environment), lamps, and baked light for phones.
Every field is in the API reference, "Lighting, water, terrain and baked light".
The default path: one Environment
import { Environment } from '@onceworlds/engine/modules';
world.spawn([Environment.preset('afternoon')]); // sky, sun with shadows, light from the sky and ground, haze
That is the whole lighting of most games. Don't add a Sky, a Light.sun or a Light.hemisphere next to it: the Environment
already makes them, matched to each other. In a scene, it is a node of kind Environment3D; a scene file keeps the hour, not the
preset's name: "Environment": { "hour": 15.5 }.
| Preset | Hour | Use it for |
|---|---|---|
noon |
12 | Bright and clear: arcade, sports, kids' games. Shadows short and sharp. |
morning, afternoon |
9, 15.5 | The safe choice for most games: warm light from the side, long enough shadows to read shapes. |
dawn, sunset, dusk |
6.5, 17.6, 18.7 | Mood: gold, orange and violet; the sun low, long shadows, more haze. |
night |
0 | Moonlight: bluish, about a quarter as bright, still readable. Add Light.point lamps for warmth. |
overcast |
12, clouds 0.85 | Grey and soft, no hard shadows. Rain, gloom, a calm puzzle. |
Tune it with fields, not more lights: heading turns where the sun rises (degrees), sun and ambient scale the sun and the sky
light, clouds (0 to 1) greys the sky and softens shadows, visibility is how far you see before the haze is complete (metres; 0
turns the fog off; a horror game wants 40 to 80), shadowRange is the half-width around the camera that gets sun shadows (smaller
is sharper).
A day passing
hour is one number: change it and the sun moves, the colours blend and night comes with the moon. Nothing jumps.
import { defineSystem } from '@onceworlds/engine';
const DayCycle = defineSystem({
name: 'day-cycle',
query: [Environment],
run({ query, time }) {
query.each((_e, env) => { env.hour = (env.hour + time.dt / 30) % 24; }); // a day in 12 minutes
},
});
In a multiplayer game set the hour from the match clock, the same on every page, instead of adding to it on each:
env.hour = (9 + time.match / 30) % 24.
Adding lights
- Lamps, fires, glowing pickups:
Light.point({ color: '#ffb35a', intensity: 40, distance: 12 })on an entity with aTransform. On phones only 6 lights are drawn and 1 casts shadows (12 and 2 on medium): keep shadows to the sun. - Your own sun or sky: a
Light.sun,Light.hemisphere,SkyorFogentity replaces that part of the Environment and keeps the rest. - No Environment and no light at all: the world is lit like noon (and the console says how to choose), so it is never black. That is a fallback, not a look: choose a preset.
Baked lighting: soft shadows that cost nothing
A bake works out once how much sky and sun reach each point of the still scenery (meshes that nothing moves, models, terrain), and lit materials read it at runtime: darkened corners, soft contact under bridges and trees, and on phones the sun's shadows without a shadow map. Use it for a level that stands still: an arena, a town, a course.
- In Build: Scene ▸ Bake Lighting. It writes
scenes/<name>.lighting.jsonbeside the scene and addsBakedLightingto the scene's root, as one undo step. Bake again after you move scenery. - In code:
const result = await bakeLighting(world), thenworld.spawn([BakedLighting(result.values)]), or savelightingFile(result)as a file and spawnBakedLighting({ file }). It takes a few seconds and yields while it works. - It is baked for one sun direction: a day passing keeps the occlusion, and a sun more than 12° away casts its own shadows again.
- Things that move are not baked, but they pick up its light as they pass through it.
Mistakes
| Don't | Do |
|---|---|
Stack a Sky, two suns and a hemisphere to brighten a dark scene |
One Environment; raise ambient or pick a brighter hour |
"Environment": { "preset": "sunset" } in a scene file |
"Environment": { "hour": 17.6 } (the loader says so when you forget) |
A shadow-casting Light.point on every torch |
Shadows from the sun only; torches without shadows |
visibility: 0 in a big open world |
Keep some haze: it gives depth and hides where the world ends |
| Bake, then move the scenery in code | Bake what stands still; let moving things take their own shadows |