Reflections, explained: probe, SSR, ray tracing

A real-time renderer has three ways to answer "what does this shiny pixel reflect?", and engines mix them. This page puts all of them in one room so you can see where each one is right, where it breaks, and what it costs on your GPU. Step through the situations below: each one sets up the camera and shows two methods side by side. Drag the divider to compare, drag the view to look around.

Pick a situation

Shadows: ...Reflections: ...
Show
Left side
Right side
Reflection denoiser
Hybrid roughness cutoff
rougher than this: probe only, no rays
Floor gloss
roughness of the floor and table top
Camera
Sculpture
Shadows
Light size
Resolution

The question every method answers

For each pixel, the renderer knows the surface: where it is, which way it faces, how rough it is. A mirror reflects one exact direction. A rough surface reflects a spread of directions around it (the "lobe"), which is why rough reflections are blurry. So the question is always: what light comes from those directions?

The noisy methods here answer it the way a path tracer would: pick one random direction in the lobe per pixel per frame, find what is there, and let a denoiser (ffxReflections()) average neighbouring pixels and previous frames into a clean image. Switch the denoiser to "raw" to see that one sample. Every mode below uses the same random directions; they differ only in how they find what is in that direction.

How each method works

Reflection probe

chair camera probe chair ignored:looks up the wall

Photograph the room once, in all six directions, from one point (a cubemap). Pre-blur copies of it for each roughness (PMREM). At draw time, look up the reflected direction. Box projection first works out where that ray would hit the room's walls, so walls line up.

good
No noise, no rays, almost free per frame. Perfect for rough surfaces.
bad
A snapshot: moving things freeze. Everything is treated as if painted on the walls, so objects inside the room reflect in the wrong place, and there's light leaking where it should be dark.
cost
A bake: 6 renders + a prefilter (about 2 ms here, on demand). Then a texture lookup per pixel.

Screen-space reflections (SSR)

top of the screen lamp left the screen

The frame you just rendered already has the colour and depth of every visible surface. Walk along the reflected ray in screen space, step by step, and stop where it passes behind a surface in the depth buffer: that pixel's colour is the reflection. three.js's SSRNode does this.

good
Exact and live for anything on screen, including moving objects. Cost doesn't depend on how big the scene is.
bad
Knows nothing that isn't on screen: off the edges, behind the camera, behind other objects, the back of things. Those need a fallback (here, the probe).
cost
A fixed amount per pixel (a few dozen depth reads), plus a denoiser.

Ray tracing

BVH boxes + shadow ray

Shoot a real ray into the scene's triangles. A BVH (a tree of boxes around the geometry, from three-mesh-bvh) lets it skip everything the ray can't hit. At the hit, shade the surface: here its colour, one shadow ray to the light, and ambient.

good
Right almost everywhere: off screen, behind the camera, hidden surfaces, moving objects.
bad
Noisy on rough surfaces. The BVH must be kept up to date when things move or deform. Shading at the hit is simplified (no textures here, one light bounce).
cost
Grows with triangle count and how many rays you shoot. Small here (27k triangles); much more in a big game level.

Hybrid

rough rug probe, no ray SSR SSR left thescreen: trace

Use each method where it's good. Rougher than a cutoff: the prefiltered probe, no ray. Glossy: try SSR first (cheap, exact on screen). Where SSR finds nothing, trace a real ray. Where that ray leaves the room, use the probe. This is AMD's FidelityFX "hybrid reflections" recipe, and roughly what Unreal and Unity's high-end pipelines do.

good
Close to "ray traced" quality, while most pixels take the cheap path.
bad
Three systems to build and keep consistent (lighting has to match between them, or you see seams).
cost
SSR everywhere glossy, rays only where SSR misses. On a GPU, rays in a few pixels of a group cost the whole group, so savings are smaller than the pixel count suggests.

Which one should I use?

probeSSR + proberay tracedhybrid
Reflects things off screenroughly, if they're on the wallsno (probe guess)yesyes
Objects inside the roommisplacedif on screenyesyes
Moving objectsfrozen until re-bakedif on screenyes (refit the BVH)yes
Rough surfacesgreatnoisynoisyuses the probe
Needs a denoisernoyesyesyes
Cost grows withnothing per frame (bake once)screen pixelspixels x scene complexitypixels, plus rays where SSR misses
GPU frame, this room3.3 ms3.9 ms4.5 ms4.2 ms

The last row: the whole frame (raster, ray-traced shadows, reflections, denoisers) on an Apple M-series GPU at 960x540, the slow orbit ("Explore"), median of 6 runs of 60 frames each; with no reflections at all it is about 3.1 ms. Runs vary by about ±0.4 ms. Run the modes yourself and read the table further down for your GPU.

Rules of thumb

  • Mostly rough or matte materials (fabric, stone, painted walls), a static scene, phones, or a product viewer on an HDRI: probes (or just the environment map). Blurry reflections hide the probe's errors.
  • Glossy floors, roads or water seen at a low angle, where what's reflected is usually on screen: SSR with probes behind it. The standard for most games.
  • Mirrors, windows, car paint, anything facing the camera, or moving objects that must reflect correctly: add rays, as a hybrid.
  • A small scene on a fast GPU and you want the simplest thing that's right: ray trace. It's the least code, but even in this small room tracing every pixel costs about 1.2 ms over the probe, against 0.9 ms for the hybrid, and one ray on a rough surface is mostly noise, where the probe is smooth and free.
  • Huge scenes, skinned characters, low-end GPUs: SSR + probes, with rays only on a few hero surfaces, if any.

In practice you stack them

Nearly every shipping engine layers these rather than picking one: probes everywhere as the base, SSR on top for glossy surfaces, and rays where the hardware allows. What differs is how far up the stack a given platform goes. The roughness cutoff is the knob between "cheap and blurry" and "exact and noisy".

Where denoising fits: anything that takes one random sample per pixel (SSR, rays, the hybrid) is noisy and needs a temporal denoiser such as ffxReflections(). Probes come pre-blurred and don't.

What it costs on your GPU

measuring...

Measured live from GPU timestamp queries for the modes selected above, at 960x540 (1080p has 4x the pixels). Passes a mode doesn't need aren't run. GPUs overlap passes, so each row is an estimate; turn the right side off and compare the GPU frame time between modes for what the reflections really cost. The probe bake is a one-off and is not in the frame time.

How this demo is built

  • three.js WebGPURenderer raster pass with a G-buffer; the panel light's shadows are ray traced (see the shadows demo).
  • Probe: the reflection probes of three.js PR #35046, ported to three r186: six renders, a GGX prefilter in a compute pass, a box-projected lookup in TSL. SSR: three's SSRNode. Rays: three-mesh-bvh's WebGPU BVH, called from a WGSL function.
  • Every noisy mode draws its one direction per pixel with the same GGX sampler three's SSR uses, so they carry the same energy.
  • Ray hits are shaded cheaply, as hybrid renderers do: albedo and metalness stored per vertex in the BVH, one shadow ray, ambient from the environment. Metals at the hit mirror the environment (no second bounce). The reference uses the same shading with many rays: it shows what the 1-sample modes should converge to, not a path-traced ground truth.

Words used on this page

Roughness
0 is a mirror, 1 is matte. Sets how wide the reflection lobe is.
Cubemap / probe
Six square renders around one point: a photo of the surroundings in every direction.
PMREM
Pre-blurred versions of a cubemap, one per roughness, so a rough reflection is one lookup.
Box projection
Correcting a probe lookup for the room's shape, so walls reflect in the right place.
Depth buffer
The distance to the visible surface at each pixel. All SSR knows about the scene.
BVH
A tree of boxes around the triangles, so a ray tests a few dozen boxes instead of every triangle.
Sample
One random direction tried for a pixel. 1 sample per pixel per frame is noisy; a denoiser fixes that.
starting...