Shaders cookbook
These snippets show the smallest useful descriptor or assembly action. Add both WGSL and GLSL source when the feature must run on both backends.
| Goal | Use | Result |
|---|---|---|
| Package reusable shader behavior | ShaderModule | Source plus typed configuration |
| Convert props to shader values | uniformTypes and getUniforms() | Uniforms and bindings for ShaderInputs |
| Declare an extension point | addShaderInjectionTarget() | A stable callable target |
| Extend a shader | inject | Ordered code at a supported target |
| Share lower-level behavior | dependencies | De-duplicated module ordering |
| Package optional behavior | ShaderPlugin | Modules, inputs, defines, and injections |
| Define an image operation | ShaderPass | A reusable fullscreen stage |
Author a module​
const tint = {
name: 'tint',
source: 'struct Tint { color: vec4f }; @group(0) @binding(auto) var<uniform> tint: Tint;',
uniformTypes: {color: 'vec4<f32>'}
} as const satisfies ShaderModule<{color: [number, number, number, number]}>;
Keep the name stable and supply a GLSL path too when WebGL 2 is supported.
Expose props and bindings​
const exposure = {
name: 'exposure',
uniformTypes: {value: 'f32'},
getUniforms: ({stops = 0}) => ({value: 2 ** stops})
} as const satisfies ShaderModule<{stops?: number}, {value: number}>;
ShaderInputs.setProps() calls getUniforms() and separates ordinary values from resource bindings.
Add a callable target​
import {WGSLShaderAssembler} from '@luma.gl/shaders/wgsl';
const assembler = new WGSLShaderAssembler();
assembler.addShaderInjectionTarget({name: 'FILTER_COLOR', stage: 'fragment', wgsl: {parameters: 'color: ptr<function, vec4<f32>>'}});
Pass this assembler to the model through its shaderAssembler prop. The application shader calls FILTER_COLOR(&color). With no contributors, the generated target is a no-op.
Inject source​
import type {ShaderPlugin} from '@luma.gl/shaders';
const fade: ShaderPlugin = {
name: 'fade',
injections: [{stage: 'fragment', target: 'FILTER_COLOR', wgsl: '(*color).a *= 0.5;'}]
};
Prefer callable targets for supported extension contracts; use built-in targets for declarations and small entry-point statements.
Compose dependencies​
const lighting = {
name: 'lighting',
dependencies: [math],
source: 'fn shade(color: vec3f) -> vec3f { return saturate(color); }'
};
Pass only direct dependencies. The assembler orders and de-duplicates the transitive graph.
Create a plugin​
const tintPlugin: ShaderPlugin = {
name: 'tint-plugin',
module: tint,
injections: [{stage: 'fragment', target: '#main-end', glsl: 'color *= tint.color;', wgsl: 'color *= tint.color;'}]
};
Attach it with new Model(device, {...props, plugins: [tintPlugin]}).
Author a pass​
const vignette = {
name: 'vignette',
source: vignetteWGSL,
fs: vignetteGLSL,
uniformTypes: {amount: 'f32'},
passes: [{sampler: true}]
} as const satisfies ShaderPass<{amount: number}>;
Execute it with Engine’s ShaderPassRenderer. Each subpass is a draw and may need an intermediate texture.