Render quality
Lighting
- Image-based lighting must be configured;
AmbientLightalone is not acceptable. The recommended setup is the built-in procedural interior — no asset files, no network request, nothing for CSP to block:const pmrem = new THREE.PMREMGenerator(renderer) const envRT = pmrem.fromScene(new RoomEnvironment()) // keep envRT — it is disposed on unmount, see Rebuild on change scene.environment = envRT.texture - Keep the environment dim (
scene.environmentIntensity ≈ 0.4as a reference; a lowerenvMapIntensityon dark parts) to hold contrast, and add one soft directional light so the lighting has a direction.
Tone mapping
ACES tone mapping must be enabled:
renderer.toneMapping = THREE.ACESFilmicToneMapping
It makes a visible difference to how MeshStandardMaterial / MeshPhysicalMaterial read.
Materials
- You should generate a procedural noise texture on a canvas and use it as both
mapandbumpMap— a cheap way to get a physical material read. Build it once and reuse it across rebuilds; Rebuild on change covers when to dispose it. ExtrudeGeometryUVs are world coordinates in mm, sotexture.repeatmust be derived from physical size: pick how many mm one tile of the texture should span, and setrepeat = 1 / tile_mm(a 45mm wood grain gives1/45). Hardcode a repeat count instead and the grain changes size whenever the part does.- The procedural material SVG used by the 2D canvas can be rendered to a canvas and reused in 3D as a
CanvasTexture, keeping 2D and 3D materials consistent. For the top face — assuming the model is centred on the origin, so world coordinates run from −R to +R —repeat = 1/(2R)andoffset = 0.5map that span onto 0–1. - You should split materials per face group,
[faceMat, sideMat](group 0 top and bottom, group 1 sides), with the sides a shade darker to read as solid.
HDRI environment (optional)
For a more convincing environment, ship a small CC0-licensed 1k .hdr (around 1.5MB): run it through PMREM for scene.environment, use the raw equirect texture as scene.background with a moderate backgroundBlurriness, and fall back to RoomEnvironment as an instant placeholder while it loads.
Two things to watch for:
- A downward-tilted camera looks at the panorama's zenith or ground — the flattest part of it — and the background collapses into one blurred colour. Rotate the horizon behind the subject with
backgroundRotationandenvironmentRotation, and start the camera at a shallower angle. - Keep the background clearly darker than the subject, so the model's silhouette and its holes stay readable.