Skip to main content

Screen-Space Reflections

Reflect visible scene lighting from glossy and rough surfaces using depth, surface normals, roughness, and temporal reprojection. createSSRCompositeShaderPass traces reflection rays through the current frame and resolves them into a stabilized specular contribution.

GitHub
0s

At a Glance​

PropertyValue
ExportcreateSSRCompositeShaderPass
BackendWebGPU
Render passesSix with velocity history; seven with camera history (adds a normal-history copy)
Required bindingsdepthTexture, normalTexture; velocity mode also requires velocityTexture
Persistent statergba16float reflections; velocity mode uses float depth, camera mode uses packed depth and normal/roughness in rgba8unorm
Reflection coverageVisible scene color and first-layer screen depth

Usage​

import {ShaderPassRenderer} from '@luma.gl/engine';
import {createSSGICompositeShaderPass, createSSRCompositeShaderPass} from '@luma.gl/engine/effects';

const renderer = new ShaderPassRenderer(device, {
colorFormat: 'rgba16float',
shaderPasses: [
createSSGICompositeShaderPass({resolutionScale: 0.5}),
createSSRCompositeShaderPass({resolutionScale: 0.5})
]
});

renderer.renderToScreen({
sourceTexture: litSceneTexture,
bindings: gBuffer.getShaderPassBindings(),
uniforms: {
ssrTrace: {
projectionMatrix,
inverseProjectionMatrix,
intensity: 1.35,
maxDistance: 60,
thickness: 0.45,
sampleCount: 48,
maxRoughness: 0.88,
frameIndex
},
ssrTemporal: {inverseProjectionMatrix, historyWeight: 0.86, depthThreshold: 0.018}
}
});

Parameters​

ParameterDefaultRange or purpose
qualityunsetfast, balanced, or detailed; omitting it preserves existing defaults.
reprojectionvelocitycamera uses camera matrices plus normal history for static geometry.
resolutionScale1 (or preset)Relative resolution of reflection tracing, history, and spatial filtering.
intensity1.35Strength of traced specular reflection before final composition.
maxDistance60Maximum view-space reflection-ray travel distance.
thickness0.45Accepted depth thickness when testing a possible screen-space hit.
sampleCount48Ray-march budget; supported range is 8 to 96.
maxRoughness0.88Highest surface roughness that can contribute reflections.
historyWeight0.86Contribution from depth-validated reflection history.
depthThreshold0.018Temporal rejection threshold for disoccluded reflected surfaces.
strength1Final ssrComposite contribution blended into scene color.

Camera-only history​

For static geometry without a velocity attachment, use createSSRCompositeShaderPass({reprojection: 'camera', quality: 'balanced'}). The factory composes ssrCameraTemporal, full-resolution packed depth and normal histories, and the existing tracing, denoising, and composition passes. Supply currentClipToPreviousClip (previous view-projection multiplied by inverse current view-projection, using WebGPU clip depth), currentViewToPreviousView, and previousInverseProjectionMatrix. Form the clip-to-clip product on the CPU to avoid reconstructing large world coordinates in the shader. Matrix conventions and units must match the depth and view-normal attachments.

The pass reads depthTexture, normalTexture, historyTexture, previousDepthTexture, and previousNormalTexture. The factory maintains three renderer history targets: RGBA16F reflection radiance, RGBA8 packed depth, and RGBA8 encoded view normals plus roughness. It writes depth with ssrCameraDepthHistoryCopy and normals with ssrNormalHistoryCopy after the temporal resolve. It initializes reflection history to zero and packed depth history to [1, 1, 1, 1]. The packed depth preserves 24 bits without requiring filterable 32-bit float textures; it is not interchangeable with the red-channel output of ssrDepthHistoryCopy.

History defaults to weight 0.8, relative depth tolerance 0.01, and normal dot threshold 0.96. Each bilinear history tap is validated separately against expected previous depth, transformed normals, and roughness before neighborhood clamping. Depth and normal histories can remain full resolution when reflection radiance is half resolution. Reset history on camera cuts, resize, material changes, and scene edits. This is camera-only reprojection: moving geometry needs motion vectors or a caller-controlled reset. Changing water normals can reject history, but reflected view-dependent radiance is still an approximation.

Quality presets​

SSR_QUALITY_PRESETS exposes the shared settings used by both reprojection modes:

QualityResolution scaleTrace samplesDenoising radiusHistory weight
fast0.253230.9
balanced0.59620.8
detailed19620.8

An explicit resolutionScale overrides the preset scale. Per-draw uniforms override the preset's tracing, filtering, and history values; for example, set history weight to zero for the first frame. Recreate the renderer to change target resolution, destroy the previous renderer, and reset camera history. Camera depth and normal histories retain full resolution. Velocity-mode depth history retains its original behavior of scaling with reflection history. These are workload choices, not measured device performance tiers. Presets do not change view-space ray distance or thickness.

Quality and Limitations​

SSR cannot reflect geometry outside the current viewport, hidden behind the first visible depth layer, or absent from the already-lit source color. Roughness-aware spatial filtering broadens reflections and rejects unrelated depth/normal surfaces. Lowering sampleCount or resolutionScale saves GPU work but can increase missed intersections or soften thin details.

Trace reflections after direct lighting and Screen-Space Global Illumination when the reflected image should include their contributions. Reset history after camera cuts or changes to attachment dimensions.