Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shader module tutorial and example #1308

Merged
merged 3 commits into from
Dec 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions docs/api-reference/shadertools/assemble-shaders.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
# Shadertools API Reference

## Functions

### `getModuleUniforms`

Takes a list of shader module names and an object with options, and creates a combined uniform object that contains all necessary uniforms for all the modules injected into your shader.
# Shader 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.

### `assembleShaders`

Expand Down
4 changes: 3 additions & 1 deletion docs/get-started/hello-instancing-high.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,9 @@ const model = new Model(gl, {
```
Note the new syntax used for the attributes. The second element in each array is an `accessor` that describes how the buffer should be traversed during a draw. luma.gl will try to infer these parameters from the data or the shader when possible, but when it can't (or when we want to override the inferred values), we have to provide an explicit accessor. We also provide the model with the number of instances we want to draw.

If all went well, running the app now should draw four triangles, each a different color. For reference the complete code is provided below:
If all went well, running the app now should draw four triangles, each a different color. See the live demo [here](/examples/getting-started/hello-instancing-mid).

For reference the complete code is provided below:
```js
import {AnimationLoop, Model} from '@luma.gl/engine';
import {Buffer, clear} from '@luma.gl/webgl';
Expand Down
4 changes: 3 additions & 1 deletion docs/get-started/hello-instancing-low.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,9 @@ requestAnimationFrame(function draw() {
});
```

If all went well, you should see the same scene as drawn by the high- and mid-level apps: four triangles of different colors. We simply used luma.gl's `shadertools` and `gltools` to provide polyfilled instanced drawing and compose our shaders from modules. The full code for the app is available below:
If all went well, you should see the same scene as drawn by the high- and mid-level apps: four triangles of different colors. See the live demo [here](/examples/getting-started/hello-instancing-low).

We simply used luma.gl's `shadertools` and `gltools` to provide polyfilled instanced drawing and compose our shaders from modules. The full code for the app is available below:
```js
import {assembleShaders} from '@luma.gl/shadertools';
import {polyfillContext} from '@luma.gl/gltools';
Expand Down
4 changes: 3 additions & 1 deletion docs/get-started/hello-instancing-mid.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ onRender({gl, program, vertexArray}) {
}
```

The scene should be identical to the one draw with the high-level API. The complete app is as follows:
The scene should be identical to the one draw with the high-level API. See the live demo [here](/examples/getting-started/hello-instancing-mid).

The complete app is as follows:
```js
import {AnimationLoop} from '@luma.gl/engine';
import {Program, VertexArray, Buffer, clear} from '@luma.gl/webgl';
Expand Down
2 changes: 1 addition & 1 deletion docs/get-started/hello-triangle.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ Our `onRender` method is comparitavely much simpler:
model.draw();
}
```
This clears the canvas and draws the `Model`. If all went well, you should see a tri-color triangle on a black background.
This clears the canvas and draws the `Model`. If all went well, you should see a tri-color triangle on a black background. See the live demo [here](/examples/getting-started/hello-triangle).

The entire application should look like the following:
```js
Expand Down
201 changes: 201 additions & 0 deletions docs/get-started/shader-modules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
# Shader Modules

This tutorial will demonstrate how to use luma.gl shader modules to make reusable bits of functionality and dynamically insert them into your shaders. Most of this will be fairly similar to the [Hello Triangle](/docs/get-started/hello-triangle.md) app.

We'll start by setting up our imports and defining our base vertex and fragment shaders:
```js
import {AnimationLoop, Model} from '@luma.gl/engine';
import {Buffer, clear} from '@luma.gl/webgl';

const vs1 = `
attribute vec2 position;

void main() {
gl_Position = vec4(position - vec2(0.5, 0.0), 0.0, 1.0);
}
`;

const fs1 = `
uniform vec3 hsvColor;

void main() {
gl_FragColor = vec4(color_hsv2rgb(hsvColor), 1.0);
}
`;

const vs2 = `
attribute vec2 position;

void main() {
gl_Position = vec4(position + vec2(0.5, 0.0), 0.0, 1.0);
}
`;

const fs2 = `
uniform vec3 hsvColor;

void main() {
gl_FragColor = vec4(color_hsv2rgb(hsvColor) - 0.3, 1.0);
}
`;
```

We have two vertex and fragment shader pairs: one will move vertices to the left, the other moves vertices to the right. Both fragment shaders take an [HSV color](https://en.wikipedia.org/wiki/HSL_and_HSV) as input call a `color_hsv2rgb` to convert it to RGB. But `color_hsv2rgb` isn't defined anywhere, so these shaders will not compile as-is.

We define `color_hsv2rgb` in a shader module:
```js
// Taken from http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
const colorModule = {
name: 'color',
fs: `
vec3 color_hsv2rgb(vec3 hsv) {
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
vec3 rgb = c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
return rgb;
}
`
};
```

Shader modules are simply JavaScript objects that contain at least a name and some shader code. They can be defined to inject code into the vertex shader, the fragment shader or both. Our `colorModule` defines the `color_hsv2rgb` function used by our fragment shaders. It converts the HSV value to RGB and returns it. We're applying a shader module best practice of prefixing our function with the module name (`color_`) to avoid name collisions.

In the `onInitialize` method of our `AnimationLoop`, we create two models with different vertex and fragment shader sources, but both including the our `colorModule`.

```js
onInitialize({gl}) {
const positionBuffer = new Buffer(gl, new Float32Array([
-0.3, -0.5,
0.3, -0.5,
0.0, 0.5
]));

const model1 = new Model(gl, {
vs: vs1,
fs: fs1,
modules: [colorModule],
attributes: {
position: positionBuffer
},
uniforms: {
hsvColor: [0.7, 1.0, 1.0]
},
vertexCount: 3
});

const model2 = new Model(gl, {
vs: vs2,
fs: fs2,
modules: [colorModule],
attributes: {
position: positionBuffer
},
uniforms: {
hsvColor: [1.0, 1.0, 1.0]
},
vertexCount: 3
});

return {model1, model2};
}
```

In `onRender`, we simply draw both models:

```js
onRender({gl, model1, model2}) {
clear(gl, {color: [0, 0, 0, 1]});
model1.draw();
model2.draw();
}
```
If all went well, a blue trangle and a red triangle should be drawn side-by-side on the canvas. See the live demo [here](/examples/getting-started/shader-modules).

Shader modules allowed us to define our HSL to RGB conversion function once and use it across multiple programs.

The entire application should look like the following:
```js
import {AnimationLoop, Model} from '@luma.gl/engine';
import {Buffer, clear} from '@luma.gl/webgl';

const vs1 = `
attribute vec2 position;

void main() {
gl_Position = vec4(position - vec2(0.5, 0.0), 0.0, 1.0);
}
`;

const fs1 = `
void main() {
gl_FragColor = color_getColor();
}
`;

const vs2 = `
attribute vec2 position;

void main() {
gl_Position = vec4(position + vec2(0.5, 0.0), 0.0, 1.0);
}
`;

const fs2 = `
void main() {
gl_FragColor = color_getColor() - 0.3;
}
`;

// Taken from http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
const colorModule = {
name: 'color',
fs: `
vec3 color_hsv2rgb(vec3 c) {
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
`
};

const loop = new AnimationLoop({
onInitialize({gl}) {
const positionBuffer = new Buffer(gl, new Float32Array([
-0.3, -0.5,
0.3, -0.5,
0.0, 0.5
]));

const model1 = new Model(gl, {
vs: vs1,
fs: fs1,
modules: [colorModule],
attributes: {
position: positionBuffer
},
vertexCount: 3
});

const model2 = new Model(gl, {
vs: vs2,
fs: fs2,
modules: [colorModule],
attributes: {
position: positionBuffer
},
vertexCount: 3
});

return {model1, model2};
},

onRender({gl, model}) {
clear(gl, {color: [0, 0, 0, 1]});
model1.draw();
model2.draw();
}
});

loop.start();
```

7 changes: 4 additions & 3 deletions docs/table-of-contents.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@
]
},
{
"title": "Getting Started",
"title": "Tutorials",
"entries": [
{ "entry": "docs/get-started/README" },
{ "entry": "docs/get-started/hello-triangle" },
{ "entry": "docs/get-started/high-medium-low" },
{ "entry": "docs/get-started/hello-instancing-high" },
{ "entry": "docs/get-started/hello-instancing-mid" },
{ "entry": "docs/get-started/hello-instancing-low" },
{ "entry": "docs/get-started/shader-modules" },
{ "entry": "docs/get-started/whats-next" }
]
},
Expand Down Expand Up @@ -104,15 +105,15 @@
"entries": [
{ "entry": "docs/developer-guide/accessors" },
{ "entry": "docs/developer-guide/transform-feedback" },
{ "entry": "docs/developer-guide/shader-modules.md" },
{ "entry": "docs/developer-guide/shader-modules" },
{ "entry": "docs/developer-guide/debugging" },
{ "entry": "docs/developer-guide/bundling" }
]
},
{
"title": "Contributor Guide",
"entries": [
{ "entry": "docs/contributor-guide/README.md" }
{ "entry": "docs/contributor-guide/README" }
]
}
]
Expand Down
Loading