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
SSR found it on screenray traced hitnothing found it: probe guessprefiltered probe, no ray
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
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)
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
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
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?
probe
SSR + probe
ray traced
hybrid
Reflects things off screen
roughly, if they're on the walls
no (probe guess)
yes
yes
Objects inside the room
misplaced
if on screen
yes
yes
Moving objects
frozen until re-baked
if on screen
yes (refit the BVH)
yes
Rough surfaces
great
noisy
noisy
uses the probe
Needs a denoiser
no
yes
yes
yes
Cost grows with
nothing per frame (bake once)
screen pixels
pixels x scene complexity
pixels, plus rays where SSR misses
GPU frame, this room
3.3 ms
3.9 ms
4.5 ms
4.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.