Skip to main content

Shadertools 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.

GoalUseResult
Package reusable shader behaviorShaderModuleSource plus typed configuration
Convert props to shader valuesuniformTypes and getUniforms()Uniforms and bindings for ShaderInputs
Declare an extension pointaddShaderHook()A stable callable hook
Extend a shaderinjectOrdered code at a supported target
Share lower-level behaviordependenciesDe-duplicated module ordering
Package optional behaviorShaderPluginModules, inputs, defines, and injections
Define an image operationShaderPassA 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 hook

const assembler = ShaderAssembler.getDefaultShaderAssembler('wgsl');
assembler.addShaderHook(
'fs:FILTER_COLOR(color: ptr<function, vec4<f32>>)'
);

The application shader calls FILTER_COLOR(&color). With no contributors, the generated hook is a no-op.

Inject source

const fade = {
name: 'fade',
inject: {
'fs:FILTER_COLOR': '(*color).a *= 0.5;'
}
};

Prefer semantic hooks for supported extension contracts; use raw source anchors sparingly.

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',
modules: [tint],
wgsl: {injections: [{target: 'fs:#main-end', injection: 'color *= tint.color;'}]},
glsl: {injections: [{target: 'fs:#main-end', injection: '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.