Skip to content

Commit

Permalink
Merge c7e1dff into d8db536
Browse files Browse the repository at this point in the history
  • Loading branch information
felixpalmer committed Jun 19, 2024
2 parents d8db536 + c7e1dff commit 17b3d74
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 31 deletions.
54 changes: 34 additions & 20 deletions docs/api-reference/shadertools/shader-module.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ For more information see [Shader Module System Guide](/docs/api-guide/shaders/sh
To define a new shader module, you create a descriptor object that brings together all the necessary pieces:

```typescript
export const MY_SHADER_MODULE = {
type ModuleProps = {
radius: number;
}

export const MY_SHADER_MODULE: ShaderModule<ModuleProps> = {
name: 'my-shader-module',
vs: '...',
fs: '...',
inject: {},
dependencies: [],
deprecations: [],
getUniforms
};
```

Expand All @@ -36,7 +39,15 @@ export const MY_SHADER_MODULE = {

- `fs` - (string | null)

#### `uniforms` (_Object_)
#### `uniformTypes` (_Object_) - Uniform shader types (note: both order and types MUST match uniform block declarations in shader)

#### `uniformPropTypes` (_Object_) - Uniform JS prop types

#### `defaultUniforms` (_Object_) - Default uniform values

#### `getUniforms` (_function_) - Function that maps props to uniforms & bindings. When not provided it is assumed that the names of the uniforms and bindings match those of the provided props

#### `defines` (_Object_) - Constant defines to be injected into shader

#### `inject` (_Object_) - injections the module will make into shader hooks, see below

Expand All @@ -52,33 +63,36 @@ If `deprecations` is supplied, `assembleShaders` will scan shader source code fo
- `deprecated`: whether the old API is still supported.


### Defining Uniforms
### Statically defining Uniforms

If the uniforms of this module can be directly pulled from user settings, they may declaratively defined by a `uniforms` object:
If the uniforms of this module can be directly pulled from user props, they may declaratively defined by a `defaultUniforms` object:

```typescript
{
name: 'my-shader-module',
uniforms: {
strength: {type: 'number', value: 1, min: 0, max: 1},
center: [0.5, 0.5]
}
defaultUniforms: {center: [0.5, 0.5], strength: 0.9},
uniformTypes: {center: 'vec2<f32>' strength: 'f32'}
}
```

At runtime, this map will be used to generate the uniforms needed by the shaders. If either `strength` or `center` is present in the user's module settings, then the user's value will be used; otherwise, the default value in the original definition will be used.

Each uniform definition may contain the following fields:
At runtime, this map will be used to generate the uniforms needed by the shaders. If either `strength` or `center` is present in the user's module props, then the user's value will be used; otherwise, the default value in the original definition will be used.

- `type` (string `number`, `boolean`, `array` or `object`
- `value` - the default value of this uniform
### Dynamically defining Uniforms

With `type: 'number'`, the following additional fields may be added for validation:
The shader module may want to perform more complex logic when mapping the user' module props to uniforms. This can be achieved using `getUniforms()`:

- `min` (_Number_)
- `max` (_Number_)

Note: `uniforms` is ignored if `getUniforms` is provided.
```typescript
{
name: 'my-shader-module',
uniformTypes: {center: 'vec2<f32>' strength: 'f32'}
getUniforms(({intensity}) => {
return {
strength: Math.sqrt(intensity),
center: intensity > 0 ? [0.5, 0.5] : [0, 0]
}
}
}
```
## Defining Injections
Expand Down Expand Up @@ -111,7 +125,7 @@ getShaderModuleUniforms(module: ShaderModule)
Each shader module provides a method to get a map of uniforms for the shader. This function will be called with two arguments:
- `opts` - the module settings to update. This argument may not be provided when `getUniforms` is called to generate a set of default uniform values.
- `opts` - the module props to update. This argument may not be provided when `getUniforms` is called to generate a set of default uniform values.
- `context` - the uniforms generated by this module's dependencies.
The function should return a JavaScript object with keys representing uniform names and values representing uniform values.
Expand Down
1 change: 1 addition & 0 deletions modules/engine/src/model/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,7 @@ export class Model {
/** Update uniform buffers from the model's shader inputs */
updateShaderInputs(): void {
this._uniformStore.setUniforms(this.shaderInputs.getUniformValues());
this.setBindings(this.shaderInputs.getBindings());
// TODO - this is already tracked through buffer/texture update times?
this.setNeedsRedraw('shaderInputs');
}
Expand Down
12 changes: 6 additions & 6 deletions modules/engine/src/shader-inputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {UniformValue, Texture, Sampler} from '@luma.gl/core';
import {log} from '@luma.gl/core';
// import type {ShaderUniformType, UniformValue, UniformFormat, UniformInfoDevice, Texture, Sampler} from '@luma.gl/core';
import {getShaderModuleDependencies, ShaderModule} from '@luma.gl/shadertools';
import {splitUniformsAndBindings} from './model/split-uniforms-and-bindings';

/** Minimal ShaderModule subset, we don't need shader code etc */
export type ShaderModuleInputs<
Expand Down Expand Up @@ -103,17 +104,16 @@ export class ShaderInputs<
}

const oldUniforms = this.moduleUniforms[moduleName];
const uniforms =
const oldBindings = this.moduleBindings[moduleName];
const uniformsAndBindings =
module.getUniforms?.(moduleProps, this.moduleUniforms[moduleName]) || (moduleProps as any);
// console.error(uniforms)

const {uniforms, bindings} = splitUniformsAndBindings(uniformsAndBindings);
this.moduleUniforms[moduleName] = {...oldUniforms, ...uniforms};
this.moduleBindings[moduleName] = {...oldBindings, ...bindings};
// // this.moduleUniformsChanged ||= moduleName;

// console.log(`setProps(${String(moduleName)}`, moduleName, this.moduleUniforms[moduleName])

// TODO - Get Module bindings
// const bindings = module.getBindings?.(moduleProps);
// this.moduleUniforms[moduleName] = bindings;
}
}

Expand Down
36 changes: 35 additions & 1 deletion modules/engine/test/shader-inputs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
// Copyright (c) vis.gl contributors

import test from 'tape-promise/tape';
import {picking} from '../../shadertools/src/index';
import {Texture} from '../../core/src/index';
import {picking, ShaderModule} from '../../shadertools/src/index';
// import {_ShaderInputs as ShaderInputs} from '@luma.gl/engine';
import {ShaderInputs} from '../src/shader-inputs';

Expand Down Expand Up @@ -62,3 +63,36 @@ test('ShaderInputs#picking prop merge', t => {

t.end();
});

test('ShaderInputs#bindings', t => {
[true, false].map(callback => {
t.comment(`custom module created ${callback ? 'with' : 'without'} getUniforms()`);
type CustomProps = {color: number[]; colorTexture: Texture};
const custom: ShaderModule<CustomProps> = {
name: 'custom',
uniformTypes: {color: 'vec3<f32>'},
uniformPropTypes: {color: {value: [0, 0, 0]}}
};
if (callback) {
custom.getUniforms = ({color, colorTexture}) => ({color, colorTexture});
}

const shaderInputs = new ShaderInputs<{
custom: CustomProps;
}>({custom});

const MOCK_TEXTURE = 'MOCK_TEXTURE' as unknown as Texture;
shaderInputs.setProps({
custom: {color: [255, 0, 0], colorTexture: MOCK_TEXTURE}
});
t.deepEqual(shaderInputs.moduleUniforms.custom.color, [255, 0, 0], 'custom color updated');
t.equal(shaderInputs.moduleBindings.custom.colorTexture, MOCK_TEXTURE, 'colorTexture updated');

const uniformValues = shaderInputs.getUniformValues();
const bindings = shaderInputs.getBindings();
t.deepEqual(uniformValues, {custom: {color: [255, 0, 0]}}, 'uniformValues correct');
t.deepEqual(bindings, {colorTexture: 'MOCK_TEXTURE'}, 'bindings correct');

t.end();
});
});
9 changes: 5 additions & 4 deletions modules/shadertools/src/lib/shader-module/shader-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Copyright (c) vis.gl contributors

import type {NumberArray} from '@math.gl/types';
import {Sampler, Texture} from '@luma.gl/core';
import type {UniformFormat} from '../../types';
import {PropType, PropValidator} from '../filters/prop-types';
import {ShaderInjection} from '../shader-assembly/shader-injections';
Expand All @@ -17,12 +18,13 @@ export type UniformInfo = {

/**
* A shader module definition object
*
* @note Needs to be initialized with `initializeShaderModules`
*/
export type ShaderModule<
PropsT extends Record<string, unknown> = Record<string, unknown>,
UniformsT extends Record<string, UniformValue> = Record<string, UniformValue>,
BindingsT extends Record<string, unknown> = {}
BindingsT extends Record<string, Buffer | Texture | Sampler> = {}
> = {
/** Used for type inference not for values */
props?: PropsT;
Expand All @@ -45,9 +47,8 @@ export type ShaderModule<
/** Default uniform values */
defaultUniforms?: Required<UniformsT>; // Record<keyof UniformsT, UniformValue>;

/** Function that maps settings to uniforms */
// getUniforms?: (settings?: Partial<SettingsT>, prevUniforms?: any /* UniformsT */) => UniformsT;
getUniforms?: (settings?: any, prevUniforms?: any) => Record<string, UniformValue>;
/** Function that maps props to uniforms & bindings */
getUniforms?: (props?: any, oldProps?: any) => Record<string, UniformValue>;

/** uniform buffers, textures, samplers, storage, ... */
bindings?: Record<keyof BindingsT, {location: number; type: 'texture' | 'sampler' | 'uniforms'}>;
Expand Down

0 comments on commit 17b3d74

Please sign in to comment.