shaderpad 1.0.0-beta.18 → 1.0.0-beta.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +367 -30
- package/dist/index.d.mts +33 -31
- package/dist/index.d.ts +33 -31
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4 -2
- package/dist/index.mjs.map +1 -1
- package/dist/plugins/face.d.mts +17 -0
- package/dist/plugins/face.d.ts +17 -0
- package/dist/plugins/face.js +12 -0
- package/dist/plugins/face.js.map +1 -0
- package/dist/plugins/face.mjs +12 -0
- package/dist/plugins/face.mjs.map +1 -0
- package/dist/plugins/hands.d.mts +15 -0
- package/dist/plugins/hands.d.ts +15 -0
- package/dist/plugins/hands.js +8 -0
- package/dist/plugins/hands.js.map +1 -0
- package/dist/plugins/hands.mjs +8 -0
- package/dist/plugins/hands.mjs.map +1 -0
- package/dist/plugins/helpers.d.mts +5 -0
- package/dist/plugins/helpers.d.ts +5 -0
- package/dist/plugins/helpers.js +7 -0
- package/dist/plugins/helpers.js.map +1 -0
- package/dist/plugins/helpers.mjs +7 -0
- package/dist/plugins/helpers.mjs.map +1 -0
- package/dist/plugins/pose.d.mts +16 -0
- package/dist/plugins/pose.d.ts +16 -0
- package/dist/plugins/pose.js +11 -0
- package/dist/plugins/pose.js.map +1 -0
- package/dist/plugins/pose.mjs +11 -0
- package/dist/plugins/pose.mjs.map +1 -0
- package/dist/plugins/save.d.mts +13 -0
- package/dist/plugins/save.d.ts +13 -0
- package/dist/plugins/save.js +2 -0
- package/dist/plugins/save.js.map +1 -0
- package/dist/plugins/save.mjs +2 -0
- package/dist/plugins/save.mjs.map +1 -0
- package/package.json +45 -2
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+

|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Minimal setup. Tiny footprint.**
|
|
4
|
+
|
|
5
|
+
ShaderPad is a lightweight, dependency-free library that reduces boilerplate when working with fragment shaders. It provides a simple interface for setup, texture and uniform management, and user interactions such as mouse movements and window resizing. It comes “batteries included” for common needs, with optional plugins — from PNG export to face/pose tracking — for more advanced use cases. Simple, performant, and portable, ShaderPad lets you focus on the fun part: writing GLSL.
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
@@ -21,7 +23,7 @@ precision highp float;
|
|
|
21
23
|
in vec2 v_uv;
|
|
22
24
|
uniform float u_time;
|
|
23
25
|
uniform vec2 u_resolution;
|
|
24
|
-
uniform
|
|
26
|
+
uniform vec2 u_cursor;
|
|
25
27
|
|
|
26
28
|
// Custom variables.
|
|
27
29
|
uniform vec3 u_cursorColor;
|
|
@@ -34,7 +36,7 @@ void main() {
|
|
|
34
36
|
float dotDist = length(dotGrid);
|
|
35
37
|
float dot = step(dotDist, 5.);
|
|
36
38
|
|
|
37
|
-
float cursorDist = distance(uv, u_cursor
|
|
39
|
+
float cursorDist = distance(uv, u_cursor * u_resolution);
|
|
38
40
|
float cursor = step(cursorDist, 25. + sin(u_time * 5.) * 5.);
|
|
39
41
|
|
|
40
42
|
vec3 color = mix(vec3(0., 0., 1.), vec3(1.), dot);
|
|
@@ -71,6 +73,173 @@ shader.play(time => {
|
|
|
71
73
|
|
|
72
74
|
See the [`examples/` directory](./examples/) for more.
|
|
73
75
|
|
|
76
|
+
## Usage
|
|
77
|
+
|
|
78
|
+
### Uniforms
|
|
79
|
+
|
|
80
|
+
#### `initializeUniform(name, type, value, options?)`
|
|
81
|
+
|
|
82
|
+
Initialize a uniform variable. The uniform must be declared in your fragment shader.
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// Initialize a float uniform.
|
|
86
|
+
shader.initializeUniform('u_speed', 'float', 1.5);
|
|
87
|
+
|
|
88
|
+
// Initialize a vec3 uniform.
|
|
89
|
+
shader.initializeUniform('u_color', 'float', [1.0, 0.5, 0.0]);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Parameters:**
|
|
93
|
+
|
|
94
|
+
- `name` (string): The name of the uniform as declared in your shader
|
|
95
|
+
- `type` ('float' | 'int'): The uniform type
|
|
96
|
+
- `value` (number | number[] | (number | number[])[]): Initial value(s)
|
|
97
|
+
- `options` (optional): `{ arrayLength?: number }` - Required for uniform arrays
|
|
98
|
+
|
|
99
|
+
#### `updateUniforms(updates, options?)`
|
|
100
|
+
|
|
101
|
+
Update one or more uniform values.
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
shader.updateUniforms({
|
|
105
|
+
u_speed: 2.0,
|
|
106
|
+
u_color: [1.0, 0.0, 0.0],
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Parameters:**
|
|
111
|
+
|
|
112
|
+
- `updates` (Record<string, number | number[] | (number | number[])[]>): Object mapping uniform names to their new values
|
|
113
|
+
- `options` (optional): `{ startIndex?: number }` - Starting index for partial array updates
|
|
114
|
+
|
|
115
|
+
#### Uniform arrays
|
|
116
|
+
|
|
117
|
+
ShaderPad supports uniform arrays. Initialize them by passing an `arrayLength` option:
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
// Initialize a vec2 array with 5 elements.
|
|
121
|
+
shader.initializeUniform(
|
|
122
|
+
'u_positions',
|
|
123
|
+
'float',
|
|
124
|
+
[
|
|
125
|
+
[0, 0],
|
|
126
|
+
[1, 1],
|
|
127
|
+
[2, 2],
|
|
128
|
+
[3, 3],
|
|
129
|
+
[4, 4],
|
|
130
|
+
],
|
|
131
|
+
{
|
|
132
|
+
arrayLength: 5,
|
|
133
|
+
}
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
// Update all elements.
|
|
137
|
+
shader.updateUniforms({
|
|
138
|
+
u_positions: [
|
|
139
|
+
[0.1, 0.2],
|
|
140
|
+
[1.1, 1.2],
|
|
141
|
+
[2.1, 2.2],
|
|
142
|
+
[3.1, 3.2],
|
|
143
|
+
[4.1, 4.2],
|
|
144
|
+
],
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Update a subset starting at index 2.
|
|
148
|
+
shader.updateUniforms(
|
|
149
|
+
{
|
|
150
|
+
u_positions: [
|
|
151
|
+
[2.5, 2.5],
|
|
152
|
+
[3.5, 3.5],
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
{ startIndex: 2 }
|
|
156
|
+
);
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
#### Included uniforms
|
|
160
|
+
|
|
161
|
+
| Uniform | Type | Description |
|
|
162
|
+
| ---------------------- | -------------- | --------------------------------------------------------------------------------- |
|
|
163
|
+
| `u_time` | float | The current time in seconds. |
|
|
164
|
+
| `u_frame` | int | The current frame number. |
|
|
165
|
+
| `u_resolution` | float[2] | The canvas element's dimensions. |
|
|
166
|
+
| `u_cursor` | float[2] | Cursor position (x, y). |
|
|
167
|
+
| `u_click` | float[3] | Click position (x, y) and left click state (z). |
|
|
168
|
+
| `u_history` | sampler2DArray | Buffer texture of prior frames. Available if `history` option is set. |
|
|
169
|
+
| `u_historyFrameOffset` | int | Frame offset for accessing history texture. Available if `history` option is set. |
|
|
170
|
+
|
|
171
|
+
#### Included varyings
|
|
172
|
+
|
|
173
|
+
| Varying | Type | Description |
|
|
174
|
+
| ------- | -------- | ----------------------------------- |
|
|
175
|
+
| `v_uv` | float[2] | The UV coordinates of the fragment. |
|
|
176
|
+
|
|
177
|
+
### Textures
|
|
178
|
+
|
|
179
|
+
#### `initializeTexture(name, source, options?)`
|
|
180
|
+
|
|
181
|
+
Initialize a texture from an image, video, or canvas element.
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
// Initialize a texture from an image.
|
|
185
|
+
const img = new Image();
|
|
186
|
+
img.src = 'texture.png';
|
|
187
|
+
img.onload = () => {
|
|
188
|
+
shader.initializeTexture('u_texture', img);
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// Initialize a texture with history (stores previous frames).
|
|
192
|
+
shader.initializeTexture('u_webcam', videoElement, { history: 30 });
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**Parameters:**
|
|
196
|
+
|
|
197
|
+
- `name` (string): The name of the texture uniform as declared in your shader
|
|
198
|
+
- `source` (HTMLImageElement | HTMLVideoElement | HTMLCanvasElement): The texture source
|
|
199
|
+
- `options` (optional): `{ history?: number }` - Number of previous frames to store
|
|
200
|
+
|
|
201
|
+
#### `updateTextures(updates)`
|
|
202
|
+
|
|
203
|
+
Update one or more textures. Useful for updating video textures each frame.
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
shader.updateTextures({
|
|
207
|
+
u_webcam: videoElement,
|
|
208
|
+
u_overlay: overlayCanvas,
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**Parameters:**
|
|
213
|
+
|
|
214
|
+
- `updates` (Record<string, TextureSource>): Object mapping texture names to their new sources
|
|
215
|
+
|
|
216
|
+
### Lifecycle methods
|
|
217
|
+
|
|
218
|
+
#### `play(callback?)`
|
|
219
|
+
|
|
220
|
+
Start the render loop. The callback is invoked each frame with the current time and frame number.
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
shader.play();
|
|
224
|
+
// Can optionally take a callback to invoke each frame.
|
|
225
|
+
shader.play((time, frame) => {
|
|
226
|
+
shader.updateTextures({ u_webcam: videoElement });
|
|
227
|
+
});
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
#### `pause()`, `reset()`, `destroy()`
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
shader.pause(); // Pause the render loop.
|
|
234
|
+
shader.reset(); // Reset frame counter and clear history buffers.
|
|
235
|
+
shader.destroy(); // Clean up resources.
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Properties
|
|
239
|
+
|
|
240
|
+
- `canvas` (HTMLCanvasElement): The canvas element used for rendering
|
|
241
|
+
- `onResize?: (width: number, height: number) => void`: Callback fired when the canvas is resized
|
|
242
|
+
|
|
74
243
|
## Options
|
|
75
244
|
|
|
76
245
|
ShaderPad’s constructor accepts an optional `options` object.
|
|
@@ -88,51 +257,219 @@ shader.onResize = (width, height) => {
|
|
|
88
257
|
};
|
|
89
258
|
```
|
|
90
259
|
|
|
91
|
-
###
|
|
260
|
+
### history
|
|
92
261
|
|
|
93
|
-
|
|
262
|
+
The `history` option enables framebuffer history, allowing you to access previous frames in your shader. Pass a number to specify how many frames to keep.
|
|
94
263
|
|
|
95
|
-
|
|
264
|
+
```typescript
|
|
265
|
+
// Store the last 10 frames of shader output.
|
|
266
|
+
const shader = new ShaderPad(fragmentShaderSrc, { history: 10 });
|
|
96
267
|
|
|
97
|
-
|
|
268
|
+
// In your shader, access history using the u_history uniform:
|
|
269
|
+
// uniform highp sampler2DArray u_history;
|
|
270
|
+
// It’s stored as a ringbuffer, so you can access specific frames with the provided offset uniform:
|
|
271
|
+
// uniform int u_historyFrameOffset;
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
You can also enable history for individual textures:
|
|
98
275
|
|
|
99
276
|
```typescript
|
|
100
|
-
|
|
277
|
+
// Store the last 30 webcam frames.
|
|
278
|
+
shader.initializeTexture('u_webcam', videoElement, { history: 30 });
|
|
279
|
+
// In your shader, access history using the u_webcam and u_webcamFrameOffset uniforms:
|
|
280
|
+
// uniform highp sampler2DArray u_webcam;
|
|
281
|
+
// uniform int u_webcamFrameOffset;
|
|
282
|
+
```
|
|
101
283
|
|
|
102
|
-
|
|
103
|
-
const shaderWithOutputHistory = new ShaderPad(fragmentShaderSrc, { plugins: [history(10)] });
|
|
284
|
+
It’s recommended to use the `helpers` plugin to simplify access to history textures:
|
|
104
285
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
286
|
+
```glsl
|
|
287
|
+
int nFramesAgo = 2; // Get the color 2 frames ago.
|
|
288
|
+
float zIndex = historyZ(u_webcam, u_webcamFrameOffset, nFramesAgo);
|
|
289
|
+
vec4 historyColor = texture(u_webcam, vec3(v_uv, zIndex));
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### plugins
|
|
293
|
+
|
|
294
|
+
ShaderPad supports plugins to add additional functionality. Plugins are imported from separate paths to keep bundle sizes small.
|
|
295
|
+
|
|
296
|
+
#### helpers
|
|
297
|
+
|
|
298
|
+
The `helpers` plugin provides convenience functions and constants. See [helpers.glsl](./src/plugins/helpers.glsl) for the implementation.
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
import ShaderPad from 'shaderpad';
|
|
302
|
+
import { helpers } from 'shaderpad/plugins/helpers';
|
|
303
|
+
|
|
304
|
+
const shader = new ShaderPad(fragmentShaderSrc, {
|
|
305
|
+
plugins: [helpers()],
|
|
306
|
+
});
|
|
108
307
|
```
|
|
109
308
|
|
|
110
309
|
#### save
|
|
111
310
|
|
|
112
|
-
The `save` plugin adds a `.save()` method to the shader that saves the current frame to a PNG file.
|
|
311
|
+
The `save` plugin adds a `.save()` method to the shader that saves the current frame to a PNG file. It works on desktop and mobile.
|
|
113
312
|
|
|
114
313
|
```typescript
|
|
115
|
-
import ShaderPad
|
|
314
|
+
import ShaderPad from 'shaderpad';
|
|
315
|
+
import { save, WithSave } from 'shaderpad/plugins/save';
|
|
316
|
+
|
|
116
317
|
const shader = new ShaderPad(fragmentShaderSrc, { plugins: [save()] }) as WithSave<ShaderPad>;
|
|
117
318
|
shader.save('my-frame');
|
|
118
319
|
```
|
|
119
320
|
|
|
120
|
-
|
|
321
|
+
#### face
|
|
121
322
|
|
|
122
|
-
|
|
123
|
-
| -------------- | -------------- | ------------------------------------------------------------------------------- |
|
|
124
|
-
| `u_time` | float | The current time in seconds. |
|
|
125
|
-
| `u_frame` | int | The current frame number. |
|
|
126
|
-
| `u_resolution` | float[2] | The canvas element's dimensions. |
|
|
127
|
-
| `u_cursor` | float[2] | Cursor position (x, y). |
|
|
128
|
-
| `u_click` | float[3] | Click position (x, y) and left click state (z). |
|
|
129
|
-
| `u_history` | sampler2DArray | Buffer texture of prior frames. Only available if the `history` plugin is used. |
|
|
323
|
+
The `face` plugin uses [MediaPipe](https://ai.google.dev/edge/mediapipe/solutions/vision/face_landmarker) to detect faces in video or image textures.
|
|
130
324
|
|
|
131
|
-
|
|
325
|
+
```typescript
|
|
326
|
+
import ShaderPad from 'shaderpad';
|
|
327
|
+
import { face } from 'shaderpad/plugins/face';
|
|
328
|
+
|
|
329
|
+
const shader = new ShaderPad(fragmentShaderSrc, {
|
|
330
|
+
plugins: [
|
|
331
|
+
face({
|
|
332
|
+
textureName: 'u_webcam',
|
|
333
|
+
options: { maxFaces: 3 },
|
|
334
|
+
}),
|
|
335
|
+
],
|
|
336
|
+
});
|
|
337
|
+
```
|
|
132
338
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
|
339
|
+
**Uniforms:**
|
|
340
|
+
|
|
341
|
+
| Uniform | Type | Description |
|
|
342
|
+
| -------------- | -------------- | ---------------------------------------------- |
|
|
343
|
+
| `u_maxFaces` | int | Maximum number of faces to detect |
|
|
344
|
+
| `u_nFaces` | int | Current number of detected faces |
|
|
345
|
+
| `u_faceCenter` | vec2[maxFaces] | Face center positions |
|
|
346
|
+
| `u_leftEye` | vec2[maxFaces] | Left eye positions |
|
|
347
|
+
| `u_rightEye` | vec2[maxFaces] | Right eye positions |
|
|
348
|
+
| `u_noseTip` | vec2[maxFaces] | Nose tip positions |
|
|
349
|
+
| `u_faceMask` | sampler2D | Face mask texture (R: mouth, G: face, B: eyes) |
|
|
350
|
+
|
|
351
|
+
**Helper functions:** `getFace(vec2 pos)`, `getEye(vec2 pos)`, `getMouth(vec2 pos)`
|
|
352
|
+
|
|
353
|
+
**Note:** The face plugin requires `@mediapipe/tasks-vision` as a peer dependency.
|
|
354
|
+
|
|
355
|
+
#### pose
|
|
356
|
+
|
|
357
|
+
The `pose` plugin uses [MediaPipe Pose Landmarker](https://ai.google.dev/edge/mediapipe/solutions/vision/pose_landmarker) to expose a flat array of 2D landmarks. Each pose contributes 33 landmarks, enumerated below.
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
import ShaderPad from 'shaderpad';
|
|
361
|
+
import { pose } from 'shaderpad/plugins/pose';
|
|
362
|
+
|
|
363
|
+
const shader = new ShaderPad(fragmentShaderSrc, {
|
|
364
|
+
plugins: [pose({ textureName: 'u_video', options: { maxPoses: 3 } })],
|
|
365
|
+
});
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
**Uniforms:**
|
|
369
|
+
|
|
370
|
+
| Uniform | Type | Description |
|
|
371
|
+
| ----------------- | ------------------- | ---------------------------------------- |
|
|
372
|
+
| `u_maxPoses` | int | Maximum number of poses to track |
|
|
373
|
+
| `u_nPoses` | int | Current number of detected poses |
|
|
374
|
+
| `u_poseLandmarks` | vec2[maxPoses * 33] | Landmark positions in UV space |
|
|
375
|
+
| `u_poseMask` | sampler2D | Pose mask texture (G: body, B: skeleton) |
|
|
376
|
+
|
|
377
|
+
**Helper functions:** `poseLandmark(int poseIndex, int landmarkIndex)`, `getBody(vec2 pos)`, `getSkeleton(vec2 pos)`
|
|
378
|
+
|
|
379
|
+
Use `poseLandmark(int poseIndex, int landmarkIndex)` in GLSL to retrieve a specific point. Landmark indices are:
|
|
380
|
+
|
|
381
|
+
| Index | Landmark | Index | Landmark |
|
|
382
|
+
| ----- | ----------------- | ----- | ---------------- |
|
|
383
|
+
| 0 | nose | 17 | left pinky |
|
|
384
|
+
| 1 | left eye (inner) | 18 | right pinky |
|
|
385
|
+
| 2 | left eye | 19 | left index |
|
|
386
|
+
| 3 | left eye (outer) | 20 | right index |
|
|
387
|
+
| 4 | right eye (inner) | 21 | left thumb |
|
|
388
|
+
| 5 | right eye | 22 | right thumb |
|
|
389
|
+
| 6 | right eye (outer) | 23 | left hip |
|
|
390
|
+
| 7 | left ear | 24 | right hip |
|
|
391
|
+
| 8 | right ear | 25 | left knee |
|
|
392
|
+
| 9 | mouth (left) | 26 | right knee |
|
|
393
|
+
| 10 | mouth (right) | 27 | left ankle |
|
|
394
|
+
| 11 | left shoulder | 28 | right ankle |
|
|
395
|
+
| 12 | right shoulder | 29 | left heel |
|
|
396
|
+
| 13 | left elbow | 30 | right heel |
|
|
397
|
+
| 14 | right elbow | 31 | left foot index |
|
|
398
|
+
| 15 | left wrist | 32 | right foot index |
|
|
399
|
+
| 16 | right wrist | | |
|
|
400
|
+
|
|
401
|
+
[Source](https://ai.google.dev/edge/mediapipe/solutions/vision/pose_landmarker#pose_landmarker_model)
|
|
402
|
+
|
|
403
|
+
A minimal fragment shader loop looks like:
|
|
404
|
+
|
|
405
|
+
```glsl
|
|
406
|
+
int POSE_LANDMARK_LEFT_HIP = 23;
|
|
407
|
+
int POSE_LANDMARK_RIGHT_HIP = 24;
|
|
408
|
+
for (int i = 0; i < u_maxPoses; ++i) {
|
|
409
|
+
if (i >= u_nPoses) break;
|
|
410
|
+
vec2 leftHip = poseLandmark(i, POSE_LANDMARK_LEFT_HIP);
|
|
411
|
+
vec2 rightHip = poseLandmark(i, POSE_LANDMARK_RIGHT_HIP);
|
|
412
|
+
// …
|
|
413
|
+
}
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
#### hands
|
|
417
|
+
|
|
418
|
+
The `hands` plugin uses [MediaPipe Hand Landmarker](https://ai.google.dev/edge/mediapipe/solutions/vision/hand_landmarker) to expose a flat array of 2D landmarks. Each hand contributes 22 landmarks, enumerated below.
|
|
419
|
+
|
|
420
|
+
```typescript
|
|
421
|
+
import ShaderPad from 'shaderpad';
|
|
422
|
+
import { hands } from 'shaderpad/plugins/hands';
|
|
423
|
+
|
|
424
|
+
const shader = new ShaderPad(fragmentShaderSrc, {
|
|
425
|
+
plugins: [hands({ textureName: 'u_video', options: { maxHands: 2 } })],
|
|
426
|
+
});
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
**Uniforms:**
|
|
430
|
+
|
|
431
|
+
| Uniform | Type | Description |
|
|
432
|
+
| ----------------- | ------------------- | -------------------------------- |
|
|
433
|
+
| `u_maxHands` | int | Maximum number of hands to track |
|
|
434
|
+
| `u_nHands` | int | Current number of detected hands |
|
|
435
|
+
| `u_handLandmarks` | vec2[maxHands * 22] | Landmark positions in UV space |
|
|
436
|
+
|
|
437
|
+
**Helper functions:** `handLandmark(int handIndex, int landmarkIndex)`
|
|
438
|
+
|
|
439
|
+
Use `handLandmark(int handIndex, int landmarkIndex)` in GLSL to retrieve a specific point. Landmark indices are:
|
|
440
|
+
|
|
441
|
+
| Index | Landmark | Index | Landmark |
|
|
442
|
+
| ----- | ----------------- | ----- | ----------------- |
|
|
443
|
+
| 0 | WRIST | 11 | MIDDLE_FINGER_DIP |
|
|
444
|
+
| 1 | THUMB_CMC | 12 | MIDDLE_FINGER_TIP |
|
|
445
|
+
| 2 | THUMB_MCP | 13 | RING_FINGER_MCP |
|
|
446
|
+
| 3 | THUMB_IP | 14 | RING_FINGER_PIP |
|
|
447
|
+
| 4 | THUMB_TIP | 15 | RING_FINGER_DIP |
|
|
448
|
+
| 5 | INDEX_FINGER_MCP | 16 | RING_FINGER_TIP |
|
|
449
|
+
| 6 | INDEX_FINGER_PIP | 17 | PINKY_MCP |
|
|
450
|
+
| 7 | INDEX_FINGER_DIP | 18 | PINKY_PIP |
|
|
451
|
+
| 8 | INDEX_FINGER_TIP | 19 | PINKY_DIP |
|
|
452
|
+
| 9 | MIDDLE_FINGER_MCP | 20 | PINKY_TIP |
|
|
453
|
+
| 10 | MIDDLE_FINGER_PIP | 21 | HAND_CENTER |
|
|
454
|
+
|
|
455
|
+
[Source](https://ai.google.dev/edge/mediapipe/solutions/vision/hand_landmarker#models)
|
|
456
|
+
|
|
457
|
+
A minimal fragment shader loop looks like:
|
|
458
|
+
|
|
459
|
+
```glsl
|
|
460
|
+
int HAND_LANDMARK_WRIST = 0;
|
|
461
|
+
int HAND_LANDMARK_THUMB_TIP = 4;
|
|
462
|
+
int HAND_LANDMARK_INDEX_TIP = 8;
|
|
463
|
+
for (int i = 0; i < u_maxHands; ++i) {
|
|
464
|
+
if (i >= u_nHands) break;
|
|
465
|
+
vec2 wrist = handLandmark(i, HAND_LANDMARK_WRIST);
|
|
466
|
+
vec2 thumbTip = handLandmark(i, HAND_LANDMARK_THUMB_TIP);
|
|
467
|
+
vec2 indexTip = handLandmark(i, HAND_LANDMARK_INDEX_TIP);
|
|
468
|
+
// …
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
**Note:** The hands plugin requires `@mediapipe/tasks-vision` as a peer dependency.
|
|
136
473
|
|
|
137
474
|
## Contributing
|
|
138
475
|
|
|
@@ -149,7 +486,7 @@ npm install
|
|
|
149
486
|
npm run dev
|
|
150
487
|
```
|
|
151
488
|
|
|
152
|
-
This will launch a local server. Open the provided URL (usually `http://localhost:5173`) in your browser to view and interact with the examples. Use the select box to view
|
|
489
|
+
This will launch a local server. Open the provided URL (usually `http://localhost:5173`) in your browser to view and interact with the examples. Use the select box to view different examples.
|
|
153
490
|
|
|
154
491
|
### Adding an example
|
|
155
492
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,50 +1,41 @@
|
|
|
1
|
-
type WithHistory<T extends ShaderPad> = T & {
|
|
2
|
-
initializeTexture(name: string, source: HTMLImageElement | HTMLVideoElement, historyDepth?: number): void;
|
|
3
|
-
};
|
|
4
|
-
declare function history(framebufferDepth?: number): (shaderPad: ShaderPad, context: PluginContext) => void;
|
|
5
|
-
|
|
6
|
-
declare module '../index' {
|
|
7
|
-
interface ShaderPad {
|
|
8
|
-
save(filename: string): Promise<void>;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
declare function save(): (shaderPad: ShaderPad, context: PluginContext) => void;
|
|
12
|
-
type WithSave<T extends ShaderPad> = T & {
|
|
13
|
-
save(filename: string): Promise<void>;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
1
|
interface Uniform {
|
|
17
2
|
type: 'float' | 'int';
|
|
18
3
|
length: 1 | 2 | 3 | 4;
|
|
19
4
|
location: WebGLUniformLocation;
|
|
5
|
+
arrayLength?: number;
|
|
20
6
|
}
|
|
21
7
|
interface Texture {
|
|
22
8
|
texture: WebGLTexture;
|
|
23
9
|
unitIndex: number;
|
|
24
|
-
|
|
25
|
-
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
history?: {
|
|
13
|
+
depth: number;
|
|
14
|
+
writeIndex: number;
|
|
15
|
+
};
|
|
26
16
|
}
|
|
27
|
-
type TextureSource = HTMLImageElement | HTMLVideoElement;
|
|
17
|
+
type TextureSource = HTMLImageElement | HTMLVideoElement | HTMLCanvasElement;
|
|
28
18
|
interface PluginContext {
|
|
29
19
|
gl: WebGL2RenderingContext;
|
|
30
20
|
uniforms: Map<string, Uniform>;
|
|
31
|
-
textures: Map<string, Texture>;
|
|
21
|
+
textures: Map<string | symbol, Texture>;
|
|
32
22
|
get program(): WebGLProgram | null;
|
|
33
23
|
canvas: HTMLCanvasElement;
|
|
34
|
-
reserveTextureUnit: (name: string) => number;
|
|
35
|
-
releaseTextureUnit: (name: string) => void;
|
|
24
|
+
reserveTextureUnit: (name: string | symbol) => number;
|
|
25
|
+
releaseTextureUnit: (name: string | symbol) => void;
|
|
26
|
+
injectGLSL: (code: string) => void;
|
|
36
27
|
}
|
|
37
28
|
type Plugin = (shaderPad: ShaderPad, context: PluginContext) => void;
|
|
38
|
-
type LifecycleMethod = 'init' | 'step' | 'destroy' | 'updateResolution' | 'reset';
|
|
29
|
+
type LifecycleMethod = 'init' | 'step' | 'destroy' | 'updateResolution' | 'reset' | 'initializeTexture' | 'updateTextures' | 'initializeUniform' | 'updateUniforms';
|
|
39
30
|
interface Options {
|
|
40
31
|
canvas?: HTMLCanvasElement | null;
|
|
41
32
|
plugins?: Plugin[];
|
|
33
|
+
history?: number;
|
|
42
34
|
}
|
|
43
35
|
declare class ShaderPad {
|
|
44
36
|
private isInternalCanvas;
|
|
45
37
|
private isTouchDevice;
|
|
46
38
|
private gl;
|
|
47
|
-
private downloadLink;
|
|
48
39
|
private fragmentShaderSrc;
|
|
49
40
|
private uniforms;
|
|
50
41
|
private textures;
|
|
@@ -65,6 +56,7 @@ declare class ShaderPad {
|
|
|
65
56
|
canvas: HTMLCanvasElement;
|
|
66
57
|
onResize?: (width: number, height: number) => void;
|
|
67
58
|
private hooks;
|
|
59
|
+
private historyDepth;
|
|
68
60
|
constructor(fragmentShaderSrc: string, options?: Options);
|
|
69
61
|
registerHook(name: LifecycleMethod, fn: Function): void;
|
|
70
62
|
private init;
|
|
@@ -74,18 +66,28 @@ declare class ShaderPad {
|
|
|
74
66
|
private handleResize;
|
|
75
67
|
private addEventListeners;
|
|
76
68
|
private updateResolution;
|
|
77
|
-
|
|
78
|
-
|
|
69
|
+
private reserveTextureUnit;
|
|
70
|
+
private releaseTextureUnit;
|
|
71
|
+
private clearHistoryTextureLayers;
|
|
72
|
+
initializeUniform(name: string, type: 'float' | 'int', value: number | number[] | (number | number[])[], options?: {
|
|
73
|
+
arrayLength?: number;
|
|
74
|
+
}): void;
|
|
75
|
+
updateUniforms(updates: Record<string, number | number[] | (number | number[])[]>, options?: {
|
|
76
|
+
startIndex?: number;
|
|
77
|
+
}): void;
|
|
78
|
+
private createTexture;
|
|
79
|
+
private _initializeTexture;
|
|
80
|
+
initializeTexture(name: string, source: TextureSource, options?: {
|
|
81
|
+
history?: number;
|
|
82
|
+
}): void;
|
|
83
|
+
updateTextures(updates: Record<string, TextureSource>): void;
|
|
84
|
+
private updateTexture;
|
|
85
|
+
draw(): void;
|
|
79
86
|
step(time: number): void;
|
|
80
87
|
play(callback?: (time: number, frame: number) => void): void;
|
|
81
88
|
pause(): void;
|
|
82
89
|
reset(): void;
|
|
83
|
-
initializeTexture(name: string, source: HTMLImageElement | HTMLVideoElement): void;
|
|
84
|
-
updateTextures(updates: Record<string, HTMLImageElement | HTMLVideoElement>): void;
|
|
85
|
-
save(filename: string): Promise<void>;
|
|
86
90
|
destroy(): void;
|
|
87
|
-
private reserveTextureUnit;
|
|
88
|
-
private releaseTextureUnit;
|
|
89
91
|
}
|
|
90
92
|
|
|
91
|
-
export { type
|
|
93
|
+
export { type Options, type PluginContext, type TextureSource, ShaderPad as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,50 +1,41 @@
|
|
|
1
|
-
type WithHistory<T extends ShaderPad> = T & {
|
|
2
|
-
initializeTexture(name: string, source: HTMLImageElement | HTMLVideoElement, historyDepth?: number): void;
|
|
3
|
-
};
|
|
4
|
-
declare function history(framebufferDepth?: number): (shaderPad: ShaderPad, context: PluginContext) => void;
|
|
5
|
-
|
|
6
|
-
declare module '../index' {
|
|
7
|
-
interface ShaderPad {
|
|
8
|
-
save(filename: string): Promise<void>;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
declare function save(): (shaderPad: ShaderPad, context: PluginContext) => void;
|
|
12
|
-
type WithSave<T extends ShaderPad> = T & {
|
|
13
|
-
save(filename: string): Promise<void>;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
1
|
interface Uniform {
|
|
17
2
|
type: 'float' | 'int';
|
|
18
3
|
length: 1 | 2 | 3 | 4;
|
|
19
4
|
location: WebGLUniformLocation;
|
|
5
|
+
arrayLength?: number;
|
|
20
6
|
}
|
|
21
7
|
interface Texture {
|
|
22
8
|
texture: WebGLTexture;
|
|
23
9
|
unitIndex: number;
|
|
24
|
-
|
|
25
|
-
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
history?: {
|
|
13
|
+
depth: number;
|
|
14
|
+
writeIndex: number;
|
|
15
|
+
};
|
|
26
16
|
}
|
|
27
|
-
type TextureSource = HTMLImageElement | HTMLVideoElement;
|
|
17
|
+
type TextureSource = HTMLImageElement | HTMLVideoElement | HTMLCanvasElement;
|
|
28
18
|
interface PluginContext {
|
|
29
19
|
gl: WebGL2RenderingContext;
|
|
30
20
|
uniforms: Map<string, Uniform>;
|
|
31
|
-
textures: Map<string, Texture>;
|
|
21
|
+
textures: Map<string | symbol, Texture>;
|
|
32
22
|
get program(): WebGLProgram | null;
|
|
33
23
|
canvas: HTMLCanvasElement;
|
|
34
|
-
reserveTextureUnit: (name: string) => number;
|
|
35
|
-
releaseTextureUnit: (name: string) => void;
|
|
24
|
+
reserveTextureUnit: (name: string | symbol) => number;
|
|
25
|
+
releaseTextureUnit: (name: string | symbol) => void;
|
|
26
|
+
injectGLSL: (code: string) => void;
|
|
36
27
|
}
|
|
37
28
|
type Plugin = (shaderPad: ShaderPad, context: PluginContext) => void;
|
|
38
|
-
type LifecycleMethod = 'init' | 'step' | 'destroy' | 'updateResolution' | 'reset';
|
|
29
|
+
type LifecycleMethod = 'init' | 'step' | 'destroy' | 'updateResolution' | 'reset' | 'initializeTexture' | 'updateTextures' | 'initializeUniform' | 'updateUniforms';
|
|
39
30
|
interface Options {
|
|
40
31
|
canvas?: HTMLCanvasElement | null;
|
|
41
32
|
plugins?: Plugin[];
|
|
33
|
+
history?: number;
|
|
42
34
|
}
|
|
43
35
|
declare class ShaderPad {
|
|
44
36
|
private isInternalCanvas;
|
|
45
37
|
private isTouchDevice;
|
|
46
38
|
private gl;
|
|
47
|
-
private downloadLink;
|
|
48
39
|
private fragmentShaderSrc;
|
|
49
40
|
private uniforms;
|
|
50
41
|
private textures;
|
|
@@ -65,6 +56,7 @@ declare class ShaderPad {
|
|
|
65
56
|
canvas: HTMLCanvasElement;
|
|
66
57
|
onResize?: (width: number, height: number) => void;
|
|
67
58
|
private hooks;
|
|
59
|
+
private historyDepth;
|
|
68
60
|
constructor(fragmentShaderSrc: string, options?: Options);
|
|
69
61
|
registerHook(name: LifecycleMethod, fn: Function): void;
|
|
70
62
|
private init;
|
|
@@ -74,18 +66,28 @@ declare class ShaderPad {
|
|
|
74
66
|
private handleResize;
|
|
75
67
|
private addEventListeners;
|
|
76
68
|
private updateResolution;
|
|
77
|
-
|
|
78
|
-
|
|
69
|
+
private reserveTextureUnit;
|
|
70
|
+
private releaseTextureUnit;
|
|
71
|
+
private clearHistoryTextureLayers;
|
|
72
|
+
initializeUniform(name: string, type: 'float' | 'int', value: number | number[] | (number | number[])[], options?: {
|
|
73
|
+
arrayLength?: number;
|
|
74
|
+
}): void;
|
|
75
|
+
updateUniforms(updates: Record<string, number | number[] | (number | number[])[]>, options?: {
|
|
76
|
+
startIndex?: number;
|
|
77
|
+
}): void;
|
|
78
|
+
private createTexture;
|
|
79
|
+
private _initializeTexture;
|
|
80
|
+
initializeTexture(name: string, source: TextureSource, options?: {
|
|
81
|
+
history?: number;
|
|
82
|
+
}): void;
|
|
83
|
+
updateTextures(updates: Record<string, TextureSource>): void;
|
|
84
|
+
private updateTexture;
|
|
85
|
+
draw(): void;
|
|
79
86
|
step(time: number): void;
|
|
80
87
|
play(callback?: (time: number, frame: number) => void): void;
|
|
81
88
|
pause(): void;
|
|
82
89
|
reset(): void;
|
|
83
|
-
initializeTexture(name: string, source: HTMLImageElement | HTMLVideoElement): void;
|
|
84
|
-
updateTextures(updates: Record<string, HTMLImageElement | HTMLVideoElement>): void;
|
|
85
|
-
save(filename: string): Promise<void>;
|
|
86
90
|
destroy(): void;
|
|
87
|
-
private reserveTextureUnit;
|
|
88
|
-
private releaseTextureUnit;
|
|
89
91
|
}
|
|
90
92
|
|
|
91
|
-
export { type
|
|
93
|
+
export { type Options, type PluginContext, type TextureSource, ShaderPad as default };
|