Skip to main content

Shader Assembly

A core luma.gl principle is that applications remain in full control of their shaders. Unlike traditional 3D engines that often generate shaders on an application's behalf, luma.gl lets applications define the main shader control flow, including:

  • Entry points, i.e. the shader main functions
  • Attributes
  • Inter-stage variables (varyings)

Shader Composition​

Applications can provide complete WGSL or GLSL shaders, but a more typical use case is to build shaders by composing reusable shader code. Shader assembly is textual composition around application-owned shader source.

@luma.gl/shaders adds reusable source and extension code before Model, Computation, or a lower-level pipeline compiles the final shader.

Assembly Pieces​

PieceOwnsUse it forReference
Application shader sourceThe shader entry points and primary flow.The WGSL source string or GLSL vs/fs strings supplied by the application.Model
ShaderInputsModule props, uniforms, and bindings after assembly.Updating typed module props and explicit resource bindings after source assembly.ShaderInputs
ShaderModuleReusable WGSL/GLSL source, uniform descriptors, bindings, and dependencies.Importing a static shader library such as picking, lighting, projection, or a helper library.ShaderModule
ShaderPluginA reusable primary module, defines, shader-facing vertex inputs, varyings, injection targets, and injections resolved for one shader language.Attaching a dynamic, optional shader extension to a Model or Computation.ShaderPlugin
Shader dependencyModule ordering.Bringing required modules in before a module that calls them.ShaderModule.dependencies
DefineConditional source selection.Backend or feature switches evaluated before assembly.WGSL Support
Callable injection targetA named callback emitted by the assembler and called by base shader source.A deliberate extension point such as OFFSET_POSITION or FILTER_COLOR.ShaderAssembler
InjectionOrdered source inserted at a callable or built-in target.Adding declarations or small flow changes without copying the base shader.ShaderAssembler

Assembly Flow​

StepWhat happens
1. Author base sourceWrite unified WGSL for WebGPU, GLSL stage strings for WebGL 2, or both.
2. Attach modules and pluginsPass modules and plugins to Model or Computation, or call ShaderAssembler directly.
3. Resolve compositionPlugin injection sources are selected for the active shader language, modules are deduplicated by name, and module dependencies are ordered before their consumers.
4. Apply definesPlatform, module, plugin, and application defines select active source before final assembly.
5. Assemble sourceModule source is prepended, callable targets are emitted, and ordered injections are applied.
6. Relocate WGSL bindingsAssembled WGSL rewrites @binding(auto) declarations to concrete binding numbers.
7. Compile and bindThe engine reflects the assembled source, creates shaders and pipelines, and uses ShaderInputs plus explicit bindings at draw or dispatch time.

Choosing The Primitive​

NeedPrefer
Import static reusable functions, uniform blocks, bindings, or dependencies into shaders.ShaderModule
Expose a callable target owned by a base shader.Injection target
Insert declarations or a short statement at a built-in target.Injection
Attach dynamic optional reusable behavior to Model or Computation.ShaderPlugin
Run a shader module as a fullscreen texture-processing stage.ShaderPass with ShaderPassRenderer

Modules and plugins are related but not interchangeable. A module is like a static library import: it contributes reusable shader code and its shader-facing data whenever the shader is assembled. A plugin is a dynamic, optional extension: it can add modules, defines, shader-facing vertex inputs, and injections when a Model or Computation opts into that behavior. Callable injection targets are the contract a base shader exposes when attached code must participate in its flow.

A plugin can provide GLSL and WGSL implementations of its targets and injections, plus vertex input and inter-stage varying definitions. This is particularly useful for portable extensions: unlike GLSL, WGSL cannot add these inputs and outputs with global declarations alone. Shader assembly updates the WGSL entry point interfaces and assigns locations as needed. The application or extension still manages buffers, accessor updates, and its own lifecycle; the plugin handles shader composition. For example, deck.gl extensions can use this shared composition path for WebGL 2 and WebGPU while retaining their existing lifecycle and attribute management.

Injection Targets​

Callable targets are named functions; built-in targets identify locations in the source. Base shaders should declare callable targets when callers need a stable semantic extension point. Use built-in targets for smaller structural additions.

TargetMeaning
vs:HOOK_NAMEVertex callable target registered with the assembler.
fs:HOOK_NAMEFragment callable target registered with the assembler.
vs:#decl, fs:#declDeclarations near the top of the matching shader stage.
vs:#main-start, fs:#main-startStatements at the start of the matching main function.
vs:#main-end, fs:#main-endStatements at the end of the matching main function.

For the authoring choices behind these extension points, continue with Shader Injection. For exact descriptor fields and examples, use the ShaderModule, ShaderPlugin, and ShaderAssembler reference pages.