luma.gl/docs/api-reference/shadertools/shader-assembler.md

8.8 KiB

ShaderAssembler

The functionality of the shadertools module shader system is primarily exposed via the ShaderAssembler class.

shaderAssebler.assembleShaders() composes base vertex and fragment shader source with

  • shader modules
  • hook functions
  • and injections to generate the final vertex and fragment shader source that can be used to create a program.

Static Methods

getDefaultShaderAssembler()

Most applications that register default modules and hooks will want to use a single Shader

Methods

addDefaultModule(module: ShaderModule)

Add a module that will automatically be added to any programs created by the program manager.

removeDefaultModule(module: ShaderModule)

Remove a module that is automatically being added to programs created by the program manager.

addShaderHook(hook: string, [opts: Object])

Creates a shader hook function that shader modules can injection code into. Shaders can call these functions, which will be no-ops by default. If a shader module injects code it will be executed upon the hook function call. This mechanism allows the application to create shaders that can be automatically extended by included shader modules.

  • hook: vs: or fs: followed by the name and arguments of the function, e.g. vs:MYHOOK_func(inout vec4 value). Hook name without arguments will also be used as the name of the shader hook
  • opts.header (optional): code always included at the beginning of a hook function
  • opts.footer (optional): code always included at the end of a hook function

assembleShaders

ahaderAssebler.assembleShaders() composes base vertex and fragment shader source with shader modules, hook functions and injections to generate the final vertex and fragment shader source that can be used to create a program.

Takes the source code of a vertex shader and a fragment shader, and a list of modules, defines, etc. Outputs resolved source code for both shaders, after adding prologue, adding defines, importing and transpiling modules, and injecting any shader fragments).

  • vs - vertex shader source
  • fs - fragment shader source code
  • id - id for the shader, will be used to inject shader names (using #define SHADER_NAME) if not already present in the source.
  • prologue=true (Boolean) - Will inject platform prologue (see below)
  • defines={} (Object) - a map of key/value pairs representing custom #defines to be injected into the shader source
  • modules=[] (Array) - list of shader modules (either objects defining the module, or names of previously registered modules)
  • inject={} (Object) - map of substituions,
  • hookFunctions=[] Array of hook functions descriptions. Descriptions can simply be the hook function signature (with a prefix vs for vertex shader, or fs for fragment shader) or an object with the hook signature, and a header and footer that will always appear in the hook function. For example:
  • transpileToGLSL100: force transpilation to GLSL ES 1.0 (see below)
[
  'vs:MY_HOOK_FUNCTION1(inout vec4 color)',
  {
    hook: 'fs:MY_HOOK_FUNCTION2(inout vec4 color)',
    header: 'if (color.a == 0.0) discard;\n',
    footer: 'color.a *= 1.2;\n'
  }
];

Returns:

  • vs - the resolved vertex shader
  • fs - the resolved fragment shader
  • getUniforms - a combined getUniforms function covering all modules.
  • moduleMap - a map with all resolved modules, keyed by name

Shader Module Assembly

luma.gl's module shader system is primarily exposed via the function assembleShaders which composes base vertex and fragment shader source with shader modules, hook functions and injections to generate the final vertex and fragment shader source that can be used to create a program.

Shader Hooks and Module Injections

Shader hooks and module injections are a system that allows for shader to be written in a generic manner, with behaviour modified when modules are included. For example if we define a shader hook as fs:MY_HOOK_FUNCTION(inout vec4 color), assembleShader will inject the following function automatically into our fragment shader:

void MY_HOOK_FUNCTION(inout vec4 color) {

}

We can the write our fragment shader as follows:

precision highp float;

void main() {
  vec4 color = vec4(1.0);
  gl_FragColor = MY_HOOK_FUNCTION(color)
}

By default, the hook function is a no-op, so this doesn't do anything. However, if we add a module injection like the following:

{
  picking: {
    'fs:VERTEX_HOOK_FUNCTION': 'color = vec4(1.0, 0.0, 0.0, 1.0);'
  }
}

And pass the picking module to assembledShaders, the hook function will be updated as follows:

void MY_HOOK_FUNCTION(inout vec4 color) {
  color = vec4(1.0, 0.0, 0.0, 1.0);
}

The hook function now changes the color from white to red.

Constants and Values

Predefined Injection Hooks

Key Shader Description
vs:#decl Vertex Inject at top of shader (declarations)
vs:#main-start Vertex Injected at the very beginning of main function
vs:#main-end Vertex Injected at the very end of main function
fs:#decl Fragment Inject at top of shader (declarations)
fs:#main-start Fragment Injected at the very beginning of main function
fs:#main-end Fragment Injected at the very end of main function

NOTE: Injections assume that the main function appears last in a shader.

Usage

Injection Map

assembleShaders (and Model constructor) will take an inject argument that contains a map of:

  • keys indicating hooks (predefined or functions)
  • values representing code to be injected. This can be either a simple string or an object containing the injection string and an order indicating its priority.

Examples:

  inject: {
    'fs:#main-end': '  gl_FragColor = picking_filterColor(gl_FragColor)'
  }
ProgramManager.getDefaultProgramManager(gl).addShaderHook('fs:MYHOOK_fragmentColor(inout vec4 color)');

new Model(gl, {
  vs,
  fs: `void main() {
    MYHOOK_fragmentColor(gl_FragColor);
  }`,
  modules: [picking]
  inject: {
    'fs:#main-start': 'gl_FragColor = vec4(1., 0., 0., 1.);';
    'fs:MYHOOK_fragmentColor': {
      injection: '  color = picking_filterColor(color);',
      order: 9999
  }
});

Transpilation

If the transpileToGLSL100 option is used, assembleShaders will attempt to transpile shaders to GLSL ES 1.0. This is a limited text replacement and requires that certain conventions be followed:

  • Statements are written one per line.
  • Only one fragment shader output is supported.
  • GLSL 3.0-only features, such as 3D textures are not supported.

Text transformations are performed according to the following tables:

Vertex Shaders

3.00 ES 1.00 ES Comment
in attribute
out varying

Fragment Shaders

3.00 ES 1.00 ES Comment
in varying
out vec4 <varName> gl_FragColor <varName> declaration is removed and usage in the code are replaced with gl_FragColor
texture texture2D texture will be replaced with texture2D to ensure 1.00 code is correct. See note on textureCube below.
textureCube * textureCube textureCube is not valid 3.00 syntax, but must be used to ensure 1.00 code is correct, because texture will be substituted with texture2D when transpiled to 100. Also textureCube will be replaced with correct texture syntax when transpiled to 300.