Environment & lighting

The SDK reads the HDRI, lights, tone mapping and exposure of your own scene and renders with them. If you want a ready-made glowing backdrop instead, see Glowing backdrops.

Light the render with your HDRI

Tell the SDK where the HDRI came from when you load it. Three.js converts an HDRI for the GPU, so the SDK cannot read the original file back from scene.environment.

import { rememberEnvironmentSource } from '@oeave/bakery3';

const hdri = await new RGBELoader().loadAsync('/studio.hdr');
rememberEnvironmentSource(hdri, '/studio.hdr');

scene.environment = hdri;

With @oeave/bakery3/r3f and drei's <Environment files> you can skip this step. useBakery3() finds the URL on its own.

You can also pass an HDRI to a single render.

await bakery3.render({ environment: '/studio.hdr' }); // a URL your page can read
await bakery3.render({ environment: hdrArrayBuffer }); // or the bytes

A URL is downloaded in your page and uploaded with the scene, once. Relative paths and cookies work, and your CORS rules apply. From a server, import the HDRI by URL.

If you build the request yourself, environment.hdri must be an uploaded asset or an HDRI from the preset library. Any other URL is refused.

If the HDRI cannot be read

The render still runs, lit only by the lights in your scene, and the compatibility report gets this warning:

⚠ environment_unavailable
The scene has an environment map, but its source pixels are not
reachable from the SDK.

Choose the background

Images are transparent by default. The HDRI still lights the scene when the background is transparent.

await bakery3.render({ background: { type: 'transparent' } }); // the default
await bakery3.render({ background: { type: 'color', color: '#f4f1ea' } });
await bakery3.render({ background: { type: 'environment' } }); // show the HDRI

Video has no transparency. A transparent background on a video gives a warning and a black background. See Render video.

Which lights render

Three.jsIn the render
DirectionalLightA sun lamp, aimed from its world transform toward its target.
PointLightA point lamp. Its light falls off physically, whatever its decay.
SpotLightA spot lamp, with angle and penumbra.
RectAreaLightAn area lamp at its width × height, facing the way it faces.
HemisphereLightThe sky color from above and the ground color from below.
AmbientLightFlat light from every direction.

Invisible lights and excluded lights are skipped, and so are lights inside a hidden group.

A point or spot light's distance and decay are not used: in the render its light always falls off with the square of the distance and never cuts off, which is three's default (decay: 2, distance: 0). check() warns when a light sets either one. A light inside a group your timeline moves stays where it was when the clip was captured; check() warns about that too.

AmbientLight and HemisphereLight add to your HDRI, as they do in Three.js. Like in Three.js, they light the matte part of a surface only, so a chrome part still reflects only the HDRI.

Make the render brighter or darker

We convert your light intensities to physical units, and that conversion is not configurable. Your viewport can still look brighter than the render, especially with pre-r155 useLegacyLights values.

advanced.lightScale multiplies every light in your scene, ambient included, by one number, so the balance between your lights stays the same. The HDRI is not affected. It keeps its own environmentIntensity.

await bakery3.render({
  quality: 'studio',
  advanced: { lightScale: 1.8 },
});

Tone mapping, exposure and color space

The SDK reads these three values from your renderer, and the render uses the same ones.

{
  outputColorSpace: 'srgb', // 'srgb' | 'srgb-linear' | 'display-p3'
  // none | linear | reinhard | cineon | aces-filmic | agx | neutral
  toneMapping: 'aces-filmic',
  toneMappingExposure: 1,
}

In React Three Fiber, useBakery3() gets your renderer from R3F automatically. On vanilla Three.js, pass it to attach().

bakery3.attach({ scene, camera, renderer });

Without a renderer, the SDK uses Three.js's own post-r155 defaults: sRGB, no tone mapping, exposure 1.

Add fog

Set three's FogExp2 on the scene and the render has fog too.

scene.fog = new THREE.FogExp2(0x0c0d10, 0.08);

In the render the fog is air that scatters light. A lamp or a glowing mesh gets a glow around it, and a spotlight shows its beam. The viewport only fades things with distance, so the render looks different where there is light.

density is three's number. 0.08 leaves about a third of the light after 12 meters. The fog's color sets the tint of the scattered light. How bright the fog looks depends on the lights in it, so a dark scene has dark fog.

Fog adds noise. If a render looks grainy, raise advanced.samples.

A linear THREE.Fog is not rendered. check() warns you about it. Use FogExp2 instead.

Use a preset HDRI or a generated sky

@oeave/bakery3/presets ships HDRIs. You get a small preview in the browser, and the render uses the full-resolution file. You do not need rememberEnvironmentSource() for them.

import { loadPresetEnvironment } from '@oeave/bakery3/presets';

scene.environment = await loadPresetEnvironment('ferndale_studio_11');

A studio HDRI looks wrong through the windows of a room. createSkyEnvironment() makes a plain sky instead: blue overhead, pale at the horizon, warm ground below. Nothing is downloaded. It has no sun, so the shadows come from the room's sun.

import { createSkyEnvironment } from '@oeave/bakery3/presets';

scene.environment = createSkyEnvironment({ intensity: 1 });

See Presets for the full list.