Presets

Drop your product into a ready-made room, stand it on a pedestal, or use our materials and HDRIs. Everything in @oeave/bakery3/presets is an ordinary Three.js object or material.

Put your product in a ready-made room

A room preset is a whole interior chosen by name: walls with windows, a floor, a pedestal, furniture and designed lighting.

import {
  createPresetRoom,
  applyPresetRoomCamera,
} from '@oeave/bakery3/presets';

const room = createPresetRoom('loft_golden');
scene.add(room);
product.position.copy(room.userData.stand); // the top of the pedestal
applyPresetRoomCamera(camera, room); // the framing the room was designed with
renderer.toneMappingExposure = room.userData.exposure; // as bright as designed
camera.far = 100; // the view outside the windows is 60 m away
camera.updateProjectionMatrix();

The rooms are atelier_skylight, beam_loft, conservatory_day, dining_golden, gallery_day, living_evening, loft_golden, noir_parlour, plaster_atelier, slat_lounge, studio_night and walnut_salon. ROOM_PRESETS lists each one with its size, defaults, stand height and camera.

The room is centered on the origin with its floor at y = 0. Move or rotate the group freely, and the render follows.

Change the light, the surfaces and the stand

const room = createPresetRoom('loft_golden', {
  lighting: 'golden', // day | golden | overcast | night
  walls: { material: 'white_plaster', color: '#e9dfd0' },
  floor: 'oak_floor',
  pedestal: 'pedestal_column', // any pedestal preset, or null for the floor
  pedestalMaterial: 'carrara_marble',
});
OptionDefaultWhat it does
lightingthe room's ownday, golden, overcast or night.
floor, walls, ceilingthe room's ownA material preset, or { material, color } to tint it.
sillthe room's ownThe material of the window sills.
pedestalthe room's ownWhat the product stands on: a pedestal preset, or null for the floor.
pedestalMaterialthe room's ownThe material of the pedestal. A room that stands its product on a table keeps the table's finish.
hdrithe room's ownThe HDRI outside the windows. Name one from HDRI_PRESETS, or false for a plain sky. It is only a backdrop: the room keeps its own light.
lightstruefalse removes the viewport lights so you can light the room yourself.
bakedtrueShows the room's baked version in the viewport, when it has one. See below.
bakefalseMakes the room itself bakeable. See below.
prebakedtrueFor a room shown as a stand-in: puts its pre-traced lightmaps on, when it has some.
baseUrlthe preset CDNLoad the room from your own host.

The group gives you these values on userData:

FieldWhat it is
standA Vector3: where the product goes.
topYThe height of the stand.
exposureThe toneMappingExposure the room was designed under.
readyA promise that resolves when the room has loaded.
shown'baked' or 'standIn': which version of the room the viewport is showing.
environmentIn a baked room: what your product reflects. Give it to scene.environment.
prebakedA promise that resolves when the pre-traced light is on, or to null when the room has none.

The render uses whatever exposure your renderer is set to. With userData.exposure the viewport and the render are both as bright as the room was designed. At exposure 1 the picture is about a stop darker.

What the render adds

The viewport shows an optimized version of the room so you can frame your product. The render builds the full room: every piece at full detail, 4k materials, leaves outside the windows that dapple the sun, haze, and the real light fixtures. Expect the render to look richer than the viewport.

  • Nothing of the room is uploaded. A product in a room uploads what the product alone would.
  • The room lights the render. scene.environment is not used while a room preset is in the scene.
  • The render is lit exactly as the room you see was. A baked room was traced under the same light a render of that lighting mode uses.
  • Set camera.far to 100 or more. The view outside hangs on a dome 60 m out, and a shorter far clips the sky through the windows to black. The console warns you when that happens.
  • Keep one room preset in the scene when you render.

The compatibility report notes the rebuilt room and the unused scene.environment as info items.

When a room is updated, the renderer keeps the version your SDK shows, so an older @oeave/bakery3 keeps rendering the room you see. If your SDK is newer than the renderer, the render uses the renderer's version of the room and carries a RoomPresetBuildFallback warning; the two may differ slightly.

Rooms that arrive already lit

Most rooms have a baked version: one optimized file with the room's traced light already in it. When the room you ask for has one, the viewport shows it, so the room looks like its render the moment it loads. You do nothing to get it.

const room = createPresetRoom('slat_lounge', { lighting: 'night' });
scene.add(room);
await room.userData.ready;

renderer.toneMappingExposure = room.userData.exposure; // the light was traced at this exposure
room.userData.shown; // 'baked'
scene.environment = room.userData.environment ?? null; // optional: the room in your product's reflections
  • Your product is lit by the room. The baked room carries a light for what the room throws back at the product, and the product's shadow falls on the floor. Turn on renderer.shadowMap.enabled and castShadow on your product to see it.
  • A render is the real room. The baked version is only what the page draws. A render builds the full room under the same light.
  • A bake covers your product only. See Bake a product in a ready-made room.

ROOM_PRESETS[name].baked lists the lighting modes that have a baked version. The viewport shows a lighter stand-in instead when:

  • the room has no baked version for that lighting,
  • you changed the room: a surface, the sill, the stand or its material, the HDRI outside, or a picture of your own,
  • you passed bake: true, lights: false or baked: false.

The stand-in is lit by ordinary viewport lights, scaled per room so the preview is about as bright as the render. If the room also has pre-traced lightmaps (ROOM_PRESETS[name].prebaked), they go on once the stand-in has loaded. await room.userData.prebaked resolves when they are on, setPresetRoomPrebaked(room, false) turns them off, and prebaked: false never downloads them.

Hang your own pictures

Rooms with a frame on the wall take your image in it.

const room = createPresetRoom('living_evening', {
  pictures: ['/art/campaign-01.jpg'],
});

Pass a URL the page can fetch, or a Blob if your shopper just uploaded one. One entry per picture the room hangs, in the order it hangs them. Use null to leave one as it came, and a short list leaves the rest.

ROOM_PRESETS[name].pictures says how many there are and which wall each is on:

ROOM_PRESETS.living_evening.pictures; // [{ wall: 'north', size: 0.7 }]

The frame does not move. Its moulding and its passepartout were built to the shape of the image the room was published with, so your picture is cropped to fill the opening, centered, the way object-fit: cover fills a box. A portrait photograph in a landscape frame is cropped top and bottom, not letterboxed.

The image is uploaded with your scene and cached by content, so hanging the same picture again uploads nothing new.

Bake the room itself

Pass bake: true and the room's floor, walls, ceiling, sills, stand and furniture are sent with your scene like any other mesh. A bake then includes them, so the shadow under your product and the bounce on the walls are baked onto the room. The renderer still provides the light. It is off by default, so a plain render uploads only the product.

const room = createPresetRoom('living_evening', { bake: true });

Build a room of your own

createRoom() builds an empty room from your sizes, materials and windows. The floor is at y = 0 and the room is centered on the origin.

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

scene.add(
  createRoom({
    width: 4, // meters. defaults are 4 × 4 × 2.7
    depth: 4,
    height: 2.7,
    floor: 'oak_floor',
    walls: 'white_plaster',
    ceiling: 'matte_paint', // null for an open ceiling, studio light from above
    wallThickness: 0.15, // the depth of the window reveals
    windows: {
      north: [{ width: 1.6 }],
      west: [{ width: 0.9, offset: -1.2 }],
    },
    sun: { elevation: 32, azimuth: 0, intensity: 3.5 },
  }),
);

North is the −z wall. A window's offset is measured to your right as you face the wall from inside. A window also takes height, sill, frame, glass and foliage.

From outside you see straight through the walls, which makes orbiting easy. They still block light.

Everything is named, so you can restyle or remove it.

floor, ceiling                                    meshes
wall-north (−z), wall-south, wall-west, wall-east groups
  └ face                                          mesh
  └ window-N                                      group (reveal-*, frame, glass, foliage)
sun                                               group (sun-light + aim target)

Let the sun in

sun: true puts a shadow-casting DirectionalLight outside the room, aimed through a window. You see the shafts of light in the viewport and in the render.

Shadows are as crisp as real sunlight by default (angleDeg: 0.53). Raise angleDeg for an overcast look. It is stored as userData.bakery3.angleDeg on the light, so you can set it on a DirectionalLight of your own too.

Every window grows leaves outside it by default, so the sun lands dappled instead of as one hard rectangle. foliage: false on a window turns them off.

Put a sky outside the windows

A studio HDRI seen through a window looks wrong, and its light is too neutral for daylight. createSkyEnvironment() makes a sky: blue overhead, pale at the horizon, warm ground below. Nothing is downloaded, and the render gets the same sky as the viewport.

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

scene.environment = createSkyEnvironment(); // { zenith, horizon, ground, intensity }
scene.add(
  createRoom({ windows: { east: { width: 2.4 } }, sun: { azimuth: 110 } }),
);

If your configurator shows the product on white, you can keep a studio HDRI in the live view and use the sky only for the render. See render-only set dressing.

Stand your product on a pedestal

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

const pedestal = createPedestal('pedestal_column', {
  material: 'carrara_marble',
});
scene.add(pedestal);
product.position.y = pedestal.userData.topY;
await pedestal.userData.ready; // wait for the model before you render

The pedestals are pedestal_block (the default), pedestal_plinth, pedestal_cylinder and pedestal_column. createPedestal({ type, material }) takes the same options as one object. Each loads at its real size in meters and takes any preset material. The furniture, plants and decor in the room presets come with the rooms and are not offered on their own.

Use a preset material

Every material comes with albedo, normal, roughness, AO and height maps, and tiles without a visible seam.

import {
  createPresetMaterial,
  loadPresetMaterial,
} from '@oeave/bakery3/presets';

const marble = createPresetMaterial('carrara_marble', {
  size: '4k', // '2k' is the default; '4k' for close-ups
  repeat: 2, // extra multiplier on the physical repeat
  ao: false, // skip the AO map
  anisotropy: 8,
});

// Or wait until every map has loaded:
const oak = await loadPresetMaterial('oak_floor');

The fifteen materials are oak_floor, walnut_floor, white_plaster, matte_paint, polished_concrete, carrara_marble, terrazzo, travertine, white_brick, coated_pine, laminate_floor, oak_veneer_05, plank_flooring_04, synthetic_wood and wooden_floor_02.

createPresetMaterial() returns a MeshStandardMaterial at once and fills in as the textures arrive. Tint any material through material.color. matte_paint is made for it.

Textures repeat at their real size. Preset rooms and pedestals have UVs in meters, and each texture repeats at 1 / tileMeters, so an oak plank is plank-sized on a 6 m floor and on a 0.4 m pedestal. Give your own geometry UVs in meters to get the same.

Light with a preset HDRI

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

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

The browser loads a small 1k preview. The render uses the 4k original, which your page never downloads or uploads. To show the HDRI behind the product, render with background: { type: 'environment' }. See Environment & lighting for backgrounds and your own HDRIs.

categoryNames
studioferndale_studio_11, ferndale_studio_12, monochrome_studio_02, studio_kontrast_04, studio_small_03
daysunny_country_road, horn-koppe_spring, grasslands_sunset, je_gray_02
goldenqwantani_sunset, venice_sunset, bambanani_sunset, umhlanga_sunrise, flamingo_pan, minedump_flats, sunset_fairway, the_sky_is_on_fire
overcastdalkey_view, afrikaans_church_exterior, quarry_cloudy
nightmoonless_golf, preller_drive, satara_night

The studio HDRIs are for product shots. The outdoor ones are what a room preset shows outside its windows in each lighting mode.

Read the catalog

import {
  MATERIAL_PRESETS,
  PEDESTAL_PRESETS,
  HDRI_PRESETS,
  ROOM_PRESETS,
} from '@oeave/bakery3/presets';

MATERIAL_PRESETS.oak_floor.tileMeters; // physical tile size
PEDESTAL_PRESETS.pedestal_column.size; // { x, y, z } in meters
HDRI_PRESETS.ferndale_studio_11.category; // 'studio'
ROOM_PRESETS.loft_golden.size; // { width, height, depth } in meters

Host the presets yourself

Presets are served from https://cdn.bakery3.com/presets by default.

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

setPresetBaseUrl('https://cdn.example.com/bakery3-presets');

createPresetMaterial(), createPedestal(), createPresetRoom() and loadPresetEnvironment() also take a baseUrl option for one call.

Where the presets come from

The HDRIs, six of the materials and some of the models in the rooms come from Poly Haven under the CC0 license: free for any use, no attribution needed. We made the pedestals and the other materials. Some of the furniture and plants in the rooms are licensed models. You see them in your renders and as a lighter stand-in in the viewport, but they are not available to download.