ShaderPassRenderer
ShaderPassRenderer applies one or more ShaderPass or ShaderPassPipeline definitions to a source texture and either renders the result back to a texture or draws it to the screen.
Internally it uses ClipSpace, BackgroundTextureModel, and SwapFramebuffers to manage the pass chain.
For the descriptor types, see
ShaderPass. For the authoring
model, see Shader Passes.
Usage
import {ShaderPassRenderer} from '@luma.gl/engine';
const renderer = new ShaderPassRenderer(device, {
shaderPasses: [myShaderPass, myShaderPassPipeline]
});
const outputTexture = renderer.renderToTexture({sourceTexture});
Per-draw uniforms and extra bindings can be supplied when a pass needs frame-specific inputs:
renderer.renderToScreen({
sourceTexture: sceneColorTexture,
bindings: {depthTexture: sceneDepthTexture},
uniforms: {
dof: {
depthRange: [0.1, 30],
focusDistance: 3,
blurCoefficient: 0.9,
pixelsPerMillimeter: 42
}
}
});
Routing Model
The renderer always provides two logical texture sources:
original: the original input texture passed torenderToTexture()previous: the current shared output of the pass chain
Plain ShaderPass objects may route subpasses only against those logical sources.
ShaderPassPipeline adds pipeline-global named render targets that any later step in that pipeline may read:
type ShaderPassPipeline<TargetNameT extends string = string> = {
name: string;
renderTargets?: Record<TargetNameT, ShaderPassRenderTarget>;
steps: ShaderPassPipelineStep<TargetNameT>[];
};
type ShaderPassRenderTarget = {
scale?: [number, number];
format?: TextureFormat;
sampler?: SamplerProps;
};
type ShaderPassPipelineStep<TargetNameT extends string = string> = {
shaderPass: ShaderPass;
inputs?: Record<string, ShaderPassInputSource<TargetNameT>>;
output?: 'previous' | TargetNameT;
uniforms?: Record<string, UniformValue>;
};
Each step runs an existing ShaderPass:
step.inputsis applied to the first subpass of the referenced pass.step.outputis applied to the last subpass of the referenced pass.step.uniformsis merged into every subpass as a base layer.
This lets the renderer orchestrate existing passes without turning ShaderPass.passes into nested effects.
Runtime Inputs
At draw time, the renderer merges three uniform layers for each shader pass:
- values already stored in
shaderInputs - uniforms declared on the pass or pipeline step
options.uniformspassed torenderToTexture()/renderToScreen()
Bindings follow a similar pattern:
- bindings already stored in
shaderInputsfor the current shader pass options.bindingspassed to the draw call
This makes it practical to keep one renderer alive while swapping in frame-specific resources such
as a freshly rendered depth texture or a VideoTexture.
When you call shaderInputs.setProps({[passName]: {...}}), any texture bindings in that object are
stored as defaults for that specific shader pass. ShaderPassRenderer resolves those defaults per
pass, then layers options.bindings on top for per-frame overrides.
Example
This example extracts highlights into one named target, runs an existing blur pass into another, then composites back to previous:
const bloomPipeline: ShaderPassPipeline<'extract' | 'blurred'> = {
name: 'bloom',
renderTargets: {
extract: {},
blurred: {scale: [0.5, 0.5]}
},
steps: [
{
shaderPass: brightExtractPass,
inputs: {sourceTexture: 'previous'},
output: 'extract',
uniforms: {threshold: 0.8}
},
{
shaderPass: gaussianBlur,
inputs: {sourceTexture: 'extract'},
output: 'blurred',
uniforms: {radius: 12}
},
{
shaderPass: bloomCompositePass,
inputs: {
sourceTexture: 'previous',
bloomTexture: 'blurred'
},
output: 'previous',
uniforms: {intensity: 1.5}
}
]
};
Using previous for the primary color input makes the pipeline compose in its declared position
in shaderPasses. original remains available for an intentional bypass of preceding effects.
Types
ShaderPassRendererProps
export type ShaderPassRendererProps = {
shaderPasses: (ShaderPass | ShaderPassPipeline)[];
shaderInputs?: ShaderInputs;
colorFormat?: TextureFormatColor;
flipY?: boolean;
};
colorFormat controls the shared previous ping-pong textures and defaults to
device.preferredColorFormat. Use rgba16float when HDR-producing passes must retain values
above 1.0 until a later tone-mapping pass.
flipY controls texture sampling in the fullscreen copies and every shader subpass. It defaults
to true on WebGPU so framebuffer attachments retain a consistent top-left screen orientation
through intermediate render targets. Set it explicitly when integrating textures with a different
origin convention.
Properties
shaderInputs
Shader-input manager used to store pass uniforms.
swapFramebuffers
Double-buffered framebuffer pair used while running the shared previous chain.
passRenderers
Internal per-entry renderers. A renderer for a ShaderPassPipeline owns that pipeline's named render targets.
textureModel
Fullscreen background-texture model used when copying or presenting results.
Methods
constructor(device: Device, props: ShaderPassRendererProps)
Initializes the shader passes, shader inputs, swap framebuffers, and presentation model.
destroy(): void
Destroys owned pass renderers, swap framebuffers, and texture model.
resize(size?: [number, number]): void
Resizes the internal swap framebuffers and all pipeline render targets to match the provided size or the current canvas size.
Named targets respect their declared scale. For example, a target with scale: [0.5, 0.5] is resized to half width and half height.
Set sampler: {minFilter: 'linear', magFilter: 'linear'} on downsampled color targets when they
will be upsampled later. Linear sampling prevents block-shaped bloom halos while leaving existing
nearest-sampled passes unchanged.
Resizing invalidates history targets whose allocation size changes.
resetHistory(): void
Invalidates every history target. The next render initializes each target from its declared
initialize value without reallocating it.
renderToScreen(options): boolean
Runs the pass chain and then draws the result into the device's current framebuffer.
renderToScreen(options: {
sourceTexture: DynamicTexture | Texture;
uniforms?: Record<string, Record<string, unknown>>;
bindings?: Record<string, Binding | TextureBindingSource>;
resetHistory?: boolean;
}): boolean
Returns false when the source texture is not ready yet.
renderToTexture(options): Texture | null
Runs the pass chain and returns the output texture.
renderToTexture(options: {
sourceTexture: DynamicTexture | Texture;
uniforms?: Record<string, Record<string, unknown>>;
bindings?: Record<string, Binding | TextureBindingSource>;
resetHistory?: boolean;
}): Texture | null
encodeToScreen(commandEncoder, options): boolean
Records the pass chain and final presentation into a caller-owned command encoder.
encodeToScreen(
commandEncoder: CommandEncoder,
options: ShaderPassRendererRenderOptions
): boolean
This method only records commands. The caller remains responsible for finishing and submitting the
encoder. The encoder must belong to the renderer's device, and no render or compute pass may be
active when this method is called. Returns false when the source texture is not ready yet.
encodeToTexture(commandEncoder, options): Texture | null
Records the pass chain into a caller-owned command encoder and returns the renderer-owned output texture.
encodeToTexture(
commandEncoder: CommandEncoder,
options: ShaderPassRendererRenderOptions
): Texture | null
This method does not finish or submit the encoder. The encoder must belong to the renderer's device, and no render or compute pass may be active. The returned texture is valid after the recorded command buffer has been submitted.
Remarks
sourceTexturemay be aDynamicTextureor a readyTexture.uniformsmay supply per-draw shader module uniforms keyed by shader pass name.bindingsmay supply per-draw texture bindings or texture binding sources keyed by shader binding name.renderToScreen()andrenderToTexture()are convenience wrappers that record ontodevice.commandEncoder; use theencodeTo*()variants when composing with a command graph or another caller-owned encoder.- Two internal framebuffers are used for ping-pong rendering through the shared
previoussequence. - Named render targets are declared only on
ShaderPassPipeline, not onShaderPass. - Target names
originalandpreviousare reserved and may not be used as pipeline target names. - A plain
ShaderPassused outside a pipeline may only referenceoriginalandprevious. - The renderer throws if a pass or pipeline step references an unknown input source or output target.
- The renderer throws if a subpass tries to read from and write to the same named render target in one draw.
- A
historytarget is the exception: same-target reads resolve to the previous physical texture, and the write becomes visible to later steps only after that draw succeeds. - History textures swap only after the complete pass chain succeeds.