mirror of
https://github.com/visgl/luma.gl.git
synced 2025-12-08 17:36:19 +00:00
208 lines
5.1 KiB
Plaintext
208 lines
5.1 KiB
Plaintext
import {DeviceTabs} from '@site/src/react-luma';
|
|
import {ShaderModulesExample} from '@site';
|
|
|
|
# Shader Modules
|
|
|
|
<DeviceTabs />
|
|
<ShaderModulesExample />
|
|
|
|
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/tutorials//hello-triangle) app.
|
|
|
|
We'll start by setting up our imports and defining our base vertex and fragment shaders:
|
|
|
|
```typescript
|
|
import {AnimationLoop, Model} from '@luma.gl/engine';
|
|
|
|
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:
|
|
|
|
```typescript
|
|
// 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);
|
|
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`.
|
|
|
|
```typescript
|
|
override onInitialize({device}) {
|
|
const positionBuffer = new Buffer(device, new Float32Array([
|
|
-0.3, -0.5,
|
|
0.3, -0.5,
|
|
0.0, 0.5
|
|
]));
|
|
|
|
const model1 = new Model(device, {
|
|
vs: vs1,
|
|
fs: fs1,
|
|
modules: [colorModule],
|
|
attributes: {
|
|
position: positionBuffer
|
|
},
|
|
uniforms: {
|
|
hsvColor: [0.7, 1.0, 1.0]
|
|
},
|
|
vertexCount: 3
|
|
});
|
|
|
|
const model2 = new Model(device, {
|
|
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:
|
|
|
|
```typescript
|
|
override onRender({device, model1, model2}) {
|
|
clear(device, {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.
|
|
|
|
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:
|
|
|
|
```typescript
|
|
import {AnimationLoop, Model} from '@luma.gl/engine';
|
|
import {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({
|
|
override onInitialize({device}) {
|
|
const positionBuffer = new Buffer(device, new Float32Array([-0.3, -0.5, 0.3, -0.5, 0.0, 0.5]));
|
|
|
|
const model1 = new Model(device, {
|
|
vs: vs1,
|
|
fs: fs1,
|
|
modules: [colorModule],
|
|
attributes: {
|
|
position: positionBuffer
|
|
},
|
|
vertexCount: 3
|
|
});
|
|
|
|
const model2 = new Model(device, {
|
|
vs: vs2,
|
|
fs: fs2,
|
|
modules: [colorModule],
|
|
attributes: {
|
|
position: positionBuffer
|
|
},
|
|
vertexCount: 3
|
|
});
|
|
|
|
return {model1, model2};
|
|
},
|
|
|
|
override onRender({device, model}) {
|
|
clear(device, {color: [0, 0, 0, 1]});
|
|
model1.draw();
|
|
model2.draw();
|
|
}
|
|
});
|
|
|
|
loop.start();
|
|
```
|