Command Palette

Search for a command to run...

Hectal
Course outline
Topic 6.5Intermediate ~21 min · step 5 of 7

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.

0/7 · 0%

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. 1

    Make a rougher metal

    In the third example change the ball's roughness: 0.25 to 0.8. The reflections blur into a soft orange glow: that is why PMREM keeps blurred copies. At 0.02 the two round light boxes appear as sharp reflections.

  2. 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 own backgroundIntensity.

Code & diagrams

Chrome with and without an environment New tab

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.

idle

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: 3
An HDR image, with and without PMREM New tab

Left: 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.

idle

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)
scene.environment, a blurred sky and a fallback New tab

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.)

idle

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: true

Break 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.

terminal
$ Run
── what you'll see ──
image: 1024 x 512 | type: 1016 (HalfFloatType is 1016) | mapping: 300
Both balls are black. There is no error and no warning in the console.

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. 1

    The problem this solves: a metal with metalness: 1 has 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. 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. 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 it RGBELoader) loads it as a half-float texture: hdr.type is HalfFloatType, 1016.

  4. 4

    **PMREMGenerator prepares 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 .texture has mapping CubeUVReflectionMapping (306). Two things matter here. The line hdr.mapping = THREE.EquirectangularReflectionMapping tells 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. Call pmrem.dispose() when you are done with the generator, not with its result.

  5. 5

    Two ways to use it. scene.environment = map lights and reflects in every Standard or Physical material that has no envMap of its own. material.envMap = map does it for one material and wins over the scene's. Strength: scene.environmentIntensity for the scene's map, material.envMapIntensity for a material's own. scene.background = hdr also shows it as the sky (with backgroundBlurriness and backgroundIntensity), but the background alone lights nothing.

  6. 6

    **No file? Use RoomEnvironment.** It is a small procedural room, built in code (no download), and pmrem.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

01

Why is a chrome ball black in a scene that has several lights?

02

What does PMREMGenerator do, and what happens if you skip it?

Practice

01

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.

02

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 background

  • Add a fallback when the environment file fails