Course outline
Environment Maps and HDR
In one line
Shiny metal shows its surroundings, not the light. An environment map is a picture of the surroundings, turned by PMREMGenerator into light and reflections. Without one, chrome is black.
Think of it like this
A mirror in an empty, dark room is black, even with a torch shining on it, because it shows only what stands in front of it. Hang a picture of a bright studio all round the room, and the mirror comes alive. The room's picture is what a metal object reflects.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- environment map
- A picture of everything around an object, used for its reflections and its soft light.
- equirectangular
- A 2:1 picture that unrolls every direction of a sphere, like a flat world map.
- HDR (high dynamic range)
- Pictures that store real brightness above 'white', so lamps stay much brighter than walls.
- PMREM
- Prefiltered mipmapped radiance environment map: the picture blurred at several levels so rough materials can read it.
Step by step
01See black chrome
The first example has two chrome balls. The left has no environment: black, with one white dot, the light's highlight. The right uses RoomEnvironment: the room, windows and floor.
02Load the HDR
await new HDRLoader().loadAsync(url) returns a texture with a 1024 × 512 half-float image. Set hdr.mapping = THREE.EquirectangularReflectionMapping so three.js knows how to read it.
03Prepare it with PMREM
new THREE.PMREMGenerator(renderer).fromEquirectangular(hdr).texture is the map the materials use. hdr.dispose() frees the original; it is no longer needed.
04Attach it
scene.environment = environment. Every Standard material in the scene now lights and reflects with it. Use environmentIntensity to balance it against your lights.
05Plan the failure
Put the load in try / catch. If the HDR fails, fall back to RoomEnvironment, so the metal is never black. The third example fails first on purpose.
Try it yourself
- 1
Make a rougher metal
In the third example change the ball's
roughness: 0.25to0.8. The reflections blur into a soft orange glow: that is why PMREM keeps blurred copies. At0.02the two round light boxes appear as sharp reflections. - 2
Dim the environment
Set
scene.environmentIntensity = 0.2;. The ball goes darker, like metal in a dim room; the sky behind it does not change, because the background has its ownbackgroundIntensity.
Code & diagrams
The left ball has no environment and only the light's dot. The right ball reflects RoomEnvironment. The count of 3 includes the lighting lookup table; the rest belongs to preparing the environment. The map's size depends on the room, not on your screen.
Console
Run the scene to see its console.Expected console output
the room map is a texture: true | size: 768 x 1024
textures in GPU memory: 3Left: the raw HDR used as envMap, with no mapping set: a black ball, and no warning. Right: after PMREMGenerator: the studio, with its two round light boxes. HDR file: Poly Haven, CC0.
Console
Run the scene to see its console.Expected console output
image: 1024 x 512 | type: 1016 (HalfFloatType is 1016) | mapping: 300
prepared map: true | mapping: 306 (CubeUVReflectionMapping is 306)The first url does not exist: the catch block warns and uses RoomEnvironment. The second works. The sky is the same picture, blurred with backgroundBlurriness. (The 404 text is trimmed so the page's own address is not printed.)
Console
Run the scene to see its console.Expected console output
environment failed, using RoomEnvironment: fetch responded with 404: Not Found
source: RoomEnvironment | scene.environment set: true
source: HDR image | scene.environment set: trueBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Use the picture without the mapping
In the second example change [raw, prepared].forEach to [raw, raw].forEach, so both balls use the picture that has no mapping set.
Myth vs fact
Myth
A bright light will make a metal shine.
Fact
A light only adds a small highlight. A metal's look comes from what it reflects, so it needs an environment. In the first example the lit left ball is still black except for one dot.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
The prefiltering costs a one-off few milliseconds and a render target. Do it once at load time and reuse the result; never in the render loop. The result is a packed 2D layout (
CubeUVReflectionMapping), not six separate faces, so it takes one texture slot. - ▸
HDR files are large. A 1k file is fine for a product showroom; 2k and 4k files give sharper reflections in smooth metal and cost more to download and to prefilter. For mobile, many teams ship the environment as a small KTX2 or an UltraHDR JPEG (
UltraHDRLoader) and still prefilter it.
Remember this
- 1
The problem this solves: a metal with
metalness: 1has almost no colour of its own. It only mirrors. A sun and a hemisphere light give it a pinpoint highlight and nothing else, so the bell and the hub look dead. Real products are photographed in studios full of soft light boxes; an environment map gives your scene one. - 2
An environment map is a picture of everything around the object. The common format is equirectangular: the whole sphere of directions unrolled into one 2:1 picture, like a world map. Looking up reads the top of the picture. three.js uses it twice: as reflections (a smooth metal shows it sharply, a rough one blurred) and as light (a matt surface is lit by the whole picture).
- 3
HDR means "more than white". A normal 8-bit picture stops at 255, so a lamp and a white wall are both 255. In a real studio the lamp is thousands of times brighter than the wall. An HDR file (
.hdr, Radiance format) stores real brightness in floating-point numbers, so highlights keep their punch.HDRLoader(older tutorials call itRGBELoader) loads it as a half-float texture:hdr.typeisHalfFloatType, 1016. - 4
**
PMREMGeneratorprepares the picture.** A rough surface needs the picture pre-blurred by different amounts.pmrem.fromEquirectangular(hdr)renders those blurred copies (a prefiltered environment) into one special render target; its.texturehasmappingCubeUVReflectionMapping(306). Two things matter here. The linehdr.mapping = THREE.EquirectangularReflectionMappingtells three.js how a direction becomes a position in the picture: skip it and the metal stays black, with no warning (the second example's left ball). With that line set, three.js can run the PMREM step itself the first time a material uses the texture (measured: a chrome ball given the mapped HDR directly reflects the studio). Doing it yourself still pays: the cost lands at load time, not inside the first frame, and you can dispose the original afterwards. Callpmrem.dispose()when you are done with the generator, not with its result. - 5
Two ways to use it.
scene.environment = maplights and reflects in every Standard or Physical material that has noenvMapof its own.material.envMap = mapdoes it for one material and wins over the scene's. Strength:scene.environmentIntensityfor the scene's map,material.envMapIntensityfor a material's own.scene.background = hdralso shows it as the sky (withbackgroundBlurrinessandbackgroundIntensity), but the background alone lights nothing. - 6
**No file? Use
RoomEnvironment.** It is a small procedural room, built in code (no download), andpmrem.fromScene(new RoomEnvironment(), 0.04)turns it into an environment in a few milliseconds. It is a good default for tests and a good fallback when the HDR fails to load, as the third example shows. The HDR file here is Poly Haven's "Studio Small 08" (CC0).
Explain it without notes
Why is a chrome ball black in a scene that has several lights?
What does PMREMGenerator do, and what happens if you skip it?
Practice
Write the lines that load /data/3d/hdr/studio_small_08_1k.hdr and make it the scene's environment, falling back to RoomEnvironment if it fails.
How do you make the sky behind the bike blurred but keep the reflections sharp?
Done when you can
Say why metal needs an environment and a lamp is not enough
Load an HDR image and prepare it with PMREMGenerator
Set
scene.environment, its intensity and a blurred backgroundAdd a fallback when the environment file fails