@fjandin/react-shader 0.0.12 → 0.0.14

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.
@@ -1 +1 @@
1
- {"version":3,"file":"frontend.d.ts","sourceRoot":"","sources":["../../src/example/frontend.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,aAAa,CAAA;AAwBpB,wBAAgB,GAAG,4CAwFlB"}
1
+ {"version":3,"file":"frontend.d.ts","sourceRoot":"","sources":["../../src/example/frontend.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,aAAa,CAAA;AAiHpB,wBAAgB,GAAG,4CAgGlB"}
@@ -0,0 +1,2 @@
1
+ export declare const textureFragment: string;
2
+ //# sourceMappingURL=texture-demo.glsl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"texture-demo.glsl.d.ts","sourceRoot":"","sources":["../../../src/example/glsl/texture-demo.glsl.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,QAsC3B,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"useWebGL.d.ts","sourceRoot":"","sources":["../../src/hooks/useWebGL.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAIvD,UAAU,eAAe;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACvC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAChC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACvC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACrC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACvC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAqED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,eAAe;;;EAyRhD"}
1
+ {"version":3,"file":"useWebGL.d.ts","sourceRoot":"","sources":["../../src/hooks/useWebGL.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAKvD,UAAU,eAAe;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACvC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAChC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACvC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACrC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACvC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAwED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,eAAe;;;EAyRhD"}
package/dist/index.cjs CHANGED
@@ -88,6 +88,147 @@ function createShaderProgram(gl, vertexSource, fragmentSource) {
88
88
  }
89
89
  }
90
90
 
91
+ // src/utils/textures.ts
92
+ function isTextureSource(value) {
93
+ if (typeof window === "undefined")
94
+ return false;
95
+ return value instanceof HTMLImageElement || value instanceof HTMLCanvasElement || value instanceof HTMLVideoElement || typeof ImageBitmap !== "undefined" && value instanceof ImageBitmap || value instanceof ImageData || typeof OffscreenCanvas !== "undefined" && value instanceof OffscreenCanvas;
96
+ }
97
+ function isTextureOptions(value) {
98
+ return typeof value === "object" && value !== null && "source" in value && isTextureSource(value.source);
99
+ }
100
+ function isTexture(value) {
101
+ return isTextureSource(value) || isTextureOptions(value);
102
+ }
103
+ function getWrapMode(gl, wrap) {
104
+ switch (wrap) {
105
+ case "repeat":
106
+ return gl.REPEAT;
107
+ case "mirror":
108
+ return gl.MIRRORED_REPEAT;
109
+ default:
110
+ return gl.CLAMP_TO_EDGE;
111
+ }
112
+ }
113
+ function getMinFilter(gl, filter) {
114
+ switch (filter) {
115
+ case "nearest":
116
+ return gl.NEAREST;
117
+ case "mipmap":
118
+ return gl.LINEAR_MIPMAP_LINEAR;
119
+ default:
120
+ return gl.LINEAR;
121
+ }
122
+ }
123
+ function getMagFilter(gl, filter) {
124
+ switch (filter) {
125
+ case "nearest":
126
+ return gl.NEAREST;
127
+ default:
128
+ return gl.LINEAR;
129
+ }
130
+ }
131
+ function isPowerOfTwo(value) {
132
+ return (value & value - 1) === 0 && value !== 0;
133
+ }
134
+ function getSourceDimensions(source) {
135
+ if (source instanceof HTMLImageElement) {
136
+ return { width: source.naturalWidth, height: source.naturalHeight };
137
+ }
138
+ if (source instanceof HTMLVideoElement) {
139
+ return { width: source.videoWidth, height: source.videoHeight };
140
+ }
141
+ if (source instanceof ImageData) {
142
+ return { width: source.width, height: source.height };
143
+ }
144
+ return { width: source.width, height: source.height };
145
+ }
146
+ function createTextureManager(gl) {
147
+ const maxUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
148
+ return {
149
+ cache: new Map,
150
+ nextUnit: 0,
151
+ maxUnits
152
+ };
153
+ }
154
+ function createTexture(gl, source, options = {}) {
155
+ const texture = gl.createTexture();
156
+ if (!texture) {
157
+ throw new Error("Failed to create WebGL texture");
158
+ }
159
+ const { wrapS = "clamp", wrapT = "clamp", minFilter = "linear", magFilter = "linear", flipY = true } = options;
160
+ gl.bindTexture(gl.TEXTURE_2D, texture);
161
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
162
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
163
+ const { width, height } = getSourceDimensions(source);
164
+ const pot = isPowerOfTwo(width) && isPowerOfTwo(height);
165
+ const isWebGL2 = "texStorage2D" in gl;
166
+ const actualMinFilter = minFilter === "mipmap" && (pot || isWebGL2) ? minFilter : minFilter === "mipmap" ? "linear" : minFilter;
167
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, getWrapMode(gl, wrapS));
168
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, getWrapMode(gl, wrapT));
169
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, getMinFilter(gl, actualMinFilter));
170
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, getMagFilter(gl, magFilter));
171
+ if (actualMinFilter === "mipmap") {
172
+ gl.generateMipmap(gl.TEXTURE_2D);
173
+ }
174
+ gl.bindTexture(gl.TEXTURE_2D, null);
175
+ return texture;
176
+ }
177
+ function updateTexture(gl, texture, source, flipY = true) {
178
+ gl.bindTexture(gl.TEXTURE_2D, texture);
179
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
180
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
181
+ gl.bindTexture(gl.TEXTURE_2D, null);
182
+ }
183
+ function needsVideoUpdate(source) {
184
+ if (!(source instanceof HTMLVideoElement))
185
+ return false;
186
+ return !source.paused && !source.ended && source.readyState >= 2;
187
+ }
188
+ function bindTextureUniform(gl, program, name, value, manager, locationCache) {
189
+ const source = isTextureOptions(value) ? value.source : value;
190
+ const options = isTextureOptions(value) ? value : {};
191
+ let entry = manager.cache.get(name);
192
+ if (entry && entry.source !== source) {
193
+ gl.deleteTexture(entry.texture);
194
+ entry = undefined;
195
+ }
196
+ if (!entry) {
197
+ if (manager.nextUnit >= manager.maxUnits) {
198
+ console.warn(`Maximum texture units (${manager.maxUnits}) exceeded for uniform "${name}"`);
199
+ return;
200
+ }
201
+ const texture = createTexture(gl, source, options);
202
+ entry = {
203
+ texture,
204
+ unit: manager.nextUnit++,
205
+ source
206
+ };
207
+ manager.cache.set(name, entry);
208
+ }
209
+ if (needsVideoUpdate(source)) {
210
+ const flipY = isTextureOptions(value) ? value.flipY ?? true : true;
211
+ updateTexture(gl, entry.texture, source, flipY);
212
+ }
213
+ gl.activeTexture(gl.TEXTURE0 + entry.unit);
214
+ gl.bindTexture(gl.TEXTURE_2D, entry.texture);
215
+ let location = locationCache.get(name);
216
+ if (location === undefined) {
217
+ location = gl.getUniformLocation(program, name);
218
+ locationCache.set(name, location);
219
+ }
220
+ if (location !== null) {
221
+ gl.uniform1i(location, entry.unit);
222
+ }
223
+ }
224
+ function cleanupTextures(gl, manager) {
225
+ for (const entry of manager.cache.values()) {
226
+ gl.deleteTexture(entry.texture);
227
+ }
228
+ manager.cache.clear();
229
+ manager.nextUnit = 0;
230
+ }
231
+
91
232
  // src/utils/uniforms.ts
92
233
  var MAX_ARRAY_LENGTH = 100;
93
234
  function isVec2(value) {
@@ -139,8 +280,14 @@ function setUniform(gl, location, value) {
139
280
  function getUniformLocation(gl, program, name) {
140
281
  return gl.getUniformLocation(program, name);
141
282
  }
142
- function setUniforms(gl, program, uniforms, locationCache) {
283
+ function setUniforms(gl, program, uniforms, locationCache, textureManager) {
143
284
  for (const [name, value] of Object.entries(uniforms)) {
285
+ if (isTexture(value)) {
286
+ if (textureManager) {
287
+ bindTextureUniform(gl, program, name, value, textureManager, locationCache);
288
+ }
289
+ continue;
290
+ }
144
291
  let location = locationCache.get(name);
145
292
  if (location === undefined) {
146
293
  location = getUniformLocation(gl, program, name);
@@ -164,6 +311,9 @@ function createUniformLocationCache() {
164
311
  return new Map;
165
312
  }
166
313
  function getUniformType(value) {
314
+ if (isTexture(value)) {
315
+ return "sampler2D";
316
+ }
167
317
  if (typeof value === "number") {
168
318
  return "float";
169
319
  }
@@ -247,10 +397,12 @@ function initializeWebGL(canvas, vertexSource, fragmentSource, customUniforms) {
247
397
  program,
248
398
  positionBuffer,
249
399
  positionAttributeLocation,
250
- uniformLocationCache: createUniformLocationCache()
400
+ uniformLocationCache: createUniformLocationCache(),
401
+ textureManager: createTextureManager(gl)
251
402
  };
252
403
  }
253
404
  function cleanupWebGL(gl, state) {
405
+ cleanupTextures(gl, state.textureManager);
254
406
  gl.deleteBuffer(state.positionBuffer);
255
407
  gl.deleteProgram(state.program);
256
408
  }
@@ -303,7 +455,7 @@ function useWebGL(options) {
303
455
  const deltaTime = lastFrameTimeRef.current === 0 ? 0 : (time - lastFrameTimeRef.current) / 1000;
304
456
  lastFrameTimeRef.current = time;
305
457
  elapsedTimeRef.current += deltaTime * timeScaleRef.current;
306
- const { gl, program, positionAttributeLocation, uniformLocationCache } = state;
458
+ const { gl, program, positionAttributeLocation, uniformLocationCache, textureManager } = state;
307
459
  const elapsedTime = elapsedTimeRef.current;
308
460
  const dpr = dprRef.current;
309
461
  const displayWidth = canvas.clientWidth;
@@ -331,7 +483,7 @@ function useWebGL(options) {
331
483
  defaultUniforms.iMouseNormalized = mouseNormalizedRef.current;
332
484
  defaultUniforms.iMouseLeftDown = mouseLeftDownRef.current ? 1 : 0;
333
485
  defaultUniforms.iResolution = [canvas.width, canvas.height];
334
- setUniforms(gl, program, { ...defaultUniforms, ...uniformsRef.current }, uniformLocationCache);
486
+ setUniforms(gl, program, { ...defaultUniforms, ...uniformsRef.current }, uniformLocationCache, textureManager);
335
487
  gl.drawArrays(gl.TRIANGLES, 0, 6);
336
488
  if (onFrameRef.current) {
337
489
  onFrameRef.current({
package/dist/index.d.ts CHANGED
@@ -4,5 +4,5 @@ export { generateDistortionRippleFunction } from "./shaders/distortion-ripple";
4
4
  export { generateSceneCirclesFunction } from "./shaders/scene-circles";
5
5
  export { generateSimplexNoiseFunction } from "./shaders/simplex-noise";
6
6
  export { generateUtilsFunction } from "./shaders/utils";
7
- export type { DefaultUniforms, FloatArray, FrameInfo, ReactShaderProps, UniformValue, Vec2, Vec2Array, Vec3, Vec3Array, Vec4, Vec4Array, } from "./types";
7
+ export type { DefaultUniforms, FloatArray, FrameInfo, ReactShaderProps, TextureMagFilter, TextureMinFilter, TextureOptions, TextureSource, TextureWrap, UniformValue, Vec2, Vec2Array, Vec3, Vec3Array, Vec4, Vec4Array, } from "./types";
8
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAA;AACtE,OAAO,EAAE,gCAAgC,EAAE,MAAM,6BAA6B,CAAA;AAC9E,OAAO,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAA;AACtE,OAAO,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAA;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AACvD,YAAY,EACV,eAAe,EACf,UAAU,EACV,SAAS,EACT,gBAAgB,EAChB,YAAY,EACZ,IAAI,EACJ,SAAS,EACT,IAAI,EACJ,SAAS,EACT,IAAI,EACJ,SAAS,GACV,MAAM,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAA;AACtE,OAAO,EAAE,gCAAgC,EAAE,MAAM,6BAA6B,CAAA;AAC9E,OAAO,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAA;AACtE,OAAO,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAA;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AACvD,YAAY,EACV,eAAe,EACf,UAAU,EACV,SAAS,EACT,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,WAAW,EACX,YAAY,EACZ,IAAI,EACJ,SAAS,EACT,IAAI,EACJ,SAAS,EACT,IAAI,EACJ,SAAS,GACV,MAAM,SAAS,CAAA"}
package/dist/index.js CHANGED
@@ -48,6 +48,147 @@ function createShaderProgram(gl, vertexSource, fragmentSource) {
48
48
  }
49
49
  }
50
50
 
51
+ // src/utils/textures.ts
52
+ function isTextureSource(value) {
53
+ if (typeof window === "undefined")
54
+ return false;
55
+ return value instanceof HTMLImageElement || value instanceof HTMLCanvasElement || value instanceof HTMLVideoElement || typeof ImageBitmap !== "undefined" && value instanceof ImageBitmap || value instanceof ImageData || typeof OffscreenCanvas !== "undefined" && value instanceof OffscreenCanvas;
56
+ }
57
+ function isTextureOptions(value) {
58
+ return typeof value === "object" && value !== null && "source" in value && isTextureSource(value.source);
59
+ }
60
+ function isTexture(value) {
61
+ return isTextureSource(value) || isTextureOptions(value);
62
+ }
63
+ function getWrapMode(gl, wrap) {
64
+ switch (wrap) {
65
+ case "repeat":
66
+ return gl.REPEAT;
67
+ case "mirror":
68
+ return gl.MIRRORED_REPEAT;
69
+ default:
70
+ return gl.CLAMP_TO_EDGE;
71
+ }
72
+ }
73
+ function getMinFilter(gl, filter) {
74
+ switch (filter) {
75
+ case "nearest":
76
+ return gl.NEAREST;
77
+ case "mipmap":
78
+ return gl.LINEAR_MIPMAP_LINEAR;
79
+ default:
80
+ return gl.LINEAR;
81
+ }
82
+ }
83
+ function getMagFilter(gl, filter) {
84
+ switch (filter) {
85
+ case "nearest":
86
+ return gl.NEAREST;
87
+ default:
88
+ return gl.LINEAR;
89
+ }
90
+ }
91
+ function isPowerOfTwo(value) {
92
+ return (value & value - 1) === 0 && value !== 0;
93
+ }
94
+ function getSourceDimensions(source) {
95
+ if (source instanceof HTMLImageElement) {
96
+ return { width: source.naturalWidth, height: source.naturalHeight };
97
+ }
98
+ if (source instanceof HTMLVideoElement) {
99
+ return { width: source.videoWidth, height: source.videoHeight };
100
+ }
101
+ if (source instanceof ImageData) {
102
+ return { width: source.width, height: source.height };
103
+ }
104
+ return { width: source.width, height: source.height };
105
+ }
106
+ function createTextureManager(gl) {
107
+ const maxUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
108
+ return {
109
+ cache: new Map,
110
+ nextUnit: 0,
111
+ maxUnits
112
+ };
113
+ }
114
+ function createTexture(gl, source, options = {}) {
115
+ const texture = gl.createTexture();
116
+ if (!texture) {
117
+ throw new Error("Failed to create WebGL texture");
118
+ }
119
+ const { wrapS = "clamp", wrapT = "clamp", minFilter = "linear", magFilter = "linear", flipY = true } = options;
120
+ gl.bindTexture(gl.TEXTURE_2D, texture);
121
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
122
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
123
+ const { width, height } = getSourceDimensions(source);
124
+ const pot = isPowerOfTwo(width) && isPowerOfTwo(height);
125
+ const isWebGL2 = "texStorage2D" in gl;
126
+ const actualMinFilter = minFilter === "mipmap" && (pot || isWebGL2) ? minFilter : minFilter === "mipmap" ? "linear" : minFilter;
127
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, getWrapMode(gl, wrapS));
128
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, getWrapMode(gl, wrapT));
129
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, getMinFilter(gl, actualMinFilter));
130
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, getMagFilter(gl, magFilter));
131
+ if (actualMinFilter === "mipmap") {
132
+ gl.generateMipmap(gl.TEXTURE_2D);
133
+ }
134
+ gl.bindTexture(gl.TEXTURE_2D, null);
135
+ return texture;
136
+ }
137
+ function updateTexture(gl, texture, source, flipY = true) {
138
+ gl.bindTexture(gl.TEXTURE_2D, texture);
139
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
140
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
141
+ gl.bindTexture(gl.TEXTURE_2D, null);
142
+ }
143
+ function needsVideoUpdate(source) {
144
+ if (!(source instanceof HTMLVideoElement))
145
+ return false;
146
+ return !source.paused && !source.ended && source.readyState >= 2;
147
+ }
148
+ function bindTextureUniform(gl, program, name, value, manager, locationCache) {
149
+ const source = isTextureOptions(value) ? value.source : value;
150
+ const options = isTextureOptions(value) ? value : {};
151
+ let entry = manager.cache.get(name);
152
+ if (entry && entry.source !== source) {
153
+ gl.deleteTexture(entry.texture);
154
+ entry = undefined;
155
+ }
156
+ if (!entry) {
157
+ if (manager.nextUnit >= manager.maxUnits) {
158
+ console.warn(`Maximum texture units (${manager.maxUnits}) exceeded for uniform "${name}"`);
159
+ return;
160
+ }
161
+ const texture = createTexture(gl, source, options);
162
+ entry = {
163
+ texture,
164
+ unit: manager.nextUnit++,
165
+ source
166
+ };
167
+ manager.cache.set(name, entry);
168
+ }
169
+ if (needsVideoUpdate(source)) {
170
+ const flipY = isTextureOptions(value) ? value.flipY ?? true : true;
171
+ updateTexture(gl, entry.texture, source, flipY);
172
+ }
173
+ gl.activeTexture(gl.TEXTURE0 + entry.unit);
174
+ gl.bindTexture(gl.TEXTURE_2D, entry.texture);
175
+ let location = locationCache.get(name);
176
+ if (location === undefined) {
177
+ location = gl.getUniformLocation(program, name);
178
+ locationCache.set(name, location);
179
+ }
180
+ if (location !== null) {
181
+ gl.uniform1i(location, entry.unit);
182
+ }
183
+ }
184
+ function cleanupTextures(gl, manager) {
185
+ for (const entry of manager.cache.values()) {
186
+ gl.deleteTexture(entry.texture);
187
+ }
188
+ manager.cache.clear();
189
+ manager.nextUnit = 0;
190
+ }
191
+
51
192
  // src/utils/uniforms.ts
52
193
  var MAX_ARRAY_LENGTH = 100;
53
194
  function isVec2(value) {
@@ -99,8 +240,14 @@ function setUniform(gl, location, value) {
99
240
  function getUniformLocation(gl, program, name) {
100
241
  return gl.getUniformLocation(program, name);
101
242
  }
102
- function setUniforms(gl, program, uniforms, locationCache) {
243
+ function setUniforms(gl, program, uniforms, locationCache, textureManager) {
103
244
  for (const [name, value] of Object.entries(uniforms)) {
245
+ if (isTexture(value)) {
246
+ if (textureManager) {
247
+ bindTextureUniform(gl, program, name, value, textureManager, locationCache);
248
+ }
249
+ continue;
250
+ }
104
251
  let location = locationCache.get(name);
105
252
  if (location === undefined) {
106
253
  location = getUniformLocation(gl, program, name);
@@ -124,6 +271,9 @@ function createUniformLocationCache() {
124
271
  return new Map;
125
272
  }
126
273
  function getUniformType(value) {
274
+ if (isTexture(value)) {
275
+ return "sampler2D";
276
+ }
127
277
  if (typeof value === "number") {
128
278
  return "float";
129
279
  }
@@ -207,10 +357,12 @@ function initializeWebGL(canvas, vertexSource, fragmentSource, customUniforms) {
207
357
  program,
208
358
  positionBuffer,
209
359
  positionAttributeLocation,
210
- uniformLocationCache: createUniformLocationCache()
360
+ uniformLocationCache: createUniformLocationCache(),
361
+ textureManager: createTextureManager(gl)
211
362
  };
212
363
  }
213
364
  function cleanupWebGL(gl, state) {
365
+ cleanupTextures(gl, state.textureManager);
214
366
  gl.deleteBuffer(state.positionBuffer);
215
367
  gl.deleteProgram(state.program);
216
368
  }
@@ -263,7 +415,7 @@ function useWebGL(options) {
263
415
  const deltaTime = lastFrameTimeRef.current === 0 ? 0 : (time - lastFrameTimeRef.current) / 1000;
264
416
  lastFrameTimeRef.current = time;
265
417
  elapsedTimeRef.current += deltaTime * timeScaleRef.current;
266
- const { gl, program, positionAttributeLocation, uniformLocationCache } = state;
418
+ const { gl, program, positionAttributeLocation, uniformLocationCache, textureManager } = state;
267
419
  const elapsedTime = elapsedTimeRef.current;
268
420
  const dpr = dprRef.current;
269
421
  const displayWidth = canvas.clientWidth;
@@ -291,7 +443,7 @@ function useWebGL(options) {
291
443
  defaultUniforms.iMouseNormalized = mouseNormalizedRef.current;
292
444
  defaultUniforms.iMouseLeftDown = mouseLeftDownRef.current ? 1 : 0;
293
445
  defaultUniforms.iResolution = [canvas.width, canvas.height];
294
- setUniforms(gl, program, { ...defaultUniforms, ...uniformsRef.current }, uniformLocationCache);
446
+ setUniforms(gl, program, { ...defaultUniforms, ...uniformsRef.current }, uniformLocationCache, textureManager);
295
447
  gl.drawArrays(gl.TRIANGLES, 0, 6);
296
448
  if (onFrameRef.current) {
297
449
  onFrameRef.current({
package/dist/types.d.ts CHANGED
@@ -5,7 +5,19 @@ export type FloatArray = number[];
5
5
  export type Vec2Array = Vec2[];
6
6
  export type Vec3Array = Vec3[];
7
7
  export type Vec4Array = Vec4[];
8
- export type UniformValue = number | Vec2 | Vec3 | Vec4 | FloatArray | Vec2Array | Vec3Array | Vec4Array;
8
+ export type TextureSource = HTMLImageElement | HTMLCanvasElement | HTMLVideoElement | ImageBitmap | ImageData | OffscreenCanvas;
9
+ export type TextureWrap = "repeat" | "clamp" | "mirror";
10
+ export type TextureMinFilter = "nearest" | "linear" | "mipmap";
11
+ export type TextureMagFilter = "nearest" | "linear";
12
+ export interface TextureOptions {
13
+ source: TextureSource;
14
+ wrapS?: TextureWrap;
15
+ wrapT?: TextureWrap;
16
+ minFilter?: TextureMinFilter;
17
+ magFilter?: TextureMagFilter;
18
+ flipY?: boolean;
19
+ }
20
+ export type UniformValue = number | Vec2 | Vec3 | Vec4 | FloatArray | Vec2Array | Vec3Array | Vec4Array | TextureSource | TextureOptions;
9
21
  export interface FrameInfo {
10
22
  deltaTime: number;
11
23
  time: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AACnC,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAC3C,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAEnD,MAAM,MAAM,UAAU,GAAG,MAAM,EAAE,CAAA;AACjC,MAAM,MAAM,SAAS,GAAG,IAAI,EAAE,CAAA;AAC9B,MAAM,MAAM,SAAS,GAAG,IAAI,EAAE,CAAA;AAC9B,MAAM,MAAM,SAAS,GAAG,IAAI,EAAE,CAAA;AAE9B,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAA;AAEvG,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvB,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACjC,aAAa,EAAE,OAAO,CAAA;CACvB;AACD,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACvC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACvC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACvC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;CACtC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,IAAI,CAAA;IACZ,gBAAgB,EAAE,IAAI,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,WAAW,EAAE,IAAI,CAAA;CAClB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AACnC,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAC3C,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAEnD,MAAM,MAAM,UAAU,GAAG,MAAM,EAAE,CAAA;AACjC,MAAM,MAAM,SAAS,GAAG,IAAI,EAAE,CAAA;AAC9B,MAAM,MAAM,SAAS,GAAG,IAAI,EAAE,CAAA;AAC9B,MAAM,MAAM,SAAS,GAAG,IAAI,EAAE,CAAA;AAE9B,MAAM,MAAM,aAAa,GACrB,gBAAgB,GAChB,iBAAiB,GACjB,gBAAgB,GAChB,WAAW,GACX,SAAS,GACT,eAAe,CAAA;AAEnB,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAA;AACvD,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAC9D,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,QAAQ,CAAA;AAEnD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,aAAa,CAAA;IACrB,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,SAAS,CAAC,EAAE,gBAAgB,CAAA;IAC5B,SAAS,CAAC,EAAE,gBAAgB,CAAA;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,UAAU,GACV,SAAS,GACT,SAAS,GACT,SAAS,GACT,aAAa,GACb,cAAc,CAAA;AAElB,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvB,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACjC,aAAa,EAAE,OAAO,CAAA;CACvB;AACD,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACvC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACvC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACvC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;CACtC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,IAAI,CAAA;IACZ,gBAAgB,EAAE,IAAI,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,WAAW,EAAE,IAAI,CAAA;CAClB"}
@@ -0,0 +1,23 @@
1
+ import type { TextureOptions, TextureSource } from "../types";
2
+ type WebGLContext = WebGLRenderingContext | WebGL2RenderingContext;
3
+ interface TextureEntry {
4
+ texture: WebGLTexture;
5
+ unit: number;
6
+ source: TextureSource;
7
+ }
8
+ export interface TextureManager {
9
+ cache: Map<string, TextureEntry>;
10
+ nextUnit: number;
11
+ maxUnits: number;
12
+ }
13
+ export declare function isTextureSource(value: unknown): value is TextureSource;
14
+ export declare function isTextureOptions(value: unknown): value is TextureOptions;
15
+ export declare function isTexture(value: unknown): value is TextureSource | TextureOptions;
16
+ export declare function createTextureManager(gl: WebGLContext): TextureManager;
17
+ export declare function createTexture(gl: WebGLContext, source: TextureSource, options?: Partial<TextureOptions>): WebGLTexture;
18
+ export declare function updateTexture(gl: WebGLContext, texture: WebGLTexture, source: TextureSource, flipY?: boolean): void;
19
+ export declare function needsVideoUpdate(source: TextureSource): boolean;
20
+ export declare function bindTextureUniform(gl: WebGLContext, program: WebGLProgram, name: string, value: TextureSource | TextureOptions, manager: TextureManager, locationCache: Map<string, WebGLUniformLocation | null>): void;
21
+ export declare function cleanupTextures(gl: WebGLContext, manager: TextureManager): void;
22
+ export {};
23
+ //# sourceMappingURL=textures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"textures.d.ts","sourceRoot":"","sources":["../../src/utils/textures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsC,cAAc,EAAE,aAAa,EAAe,MAAM,UAAU,CAAA;AAE9G,KAAK,YAAY,GAAG,qBAAqB,GAAG,sBAAsB,CAAA;AAElE,UAAU,YAAY;IACpB,OAAO,EAAE,YAAY,CAAA;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,aAAa,CAAA;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IAChC,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAUtE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAOxE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,GAAG,cAAc,CAEjF;AAmDD,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,YAAY,GAAG,cAAc,CAOrE;AAED,wBAAgB,aAAa,CAC3B,EAAE,EAAE,YAAY,EAChB,MAAM,EAAE,aAAa,EACrB,OAAO,GAAE,OAAO,CAAC,cAAc,CAAM,GACpC,YAAY,CAqCd;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,UAAO,GAAG,IAAI,CAKhH;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAG/D;AAED,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,YAAY,EAChB,OAAO,EAAE,YAAY,EACrB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,aAAa,GAAG,cAAc,EACrC,OAAO,EAAE,cAAc,EACvB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC,GACtD,IAAI,CA+CN;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI,CAM/E"}
@@ -1,9 +1,10 @@
1
1
  import type { UniformValue } from "../types";
2
+ import { type TextureManager } from "./textures";
2
3
  type WebGLContext = WebGLRenderingContext | WebGL2RenderingContext;
3
4
  export declare const MAX_ARRAY_LENGTH = 100;
4
5
  export declare function setUniform(gl: WebGLContext, location: WebGLUniformLocation | null, value: UniformValue): void;
5
6
  export declare function getUniformLocation(gl: WebGLContext, program: WebGLProgram, name: string): WebGLUniformLocation | null;
6
- export declare function setUniforms(gl: WebGLContext, program: WebGLProgram, uniforms: Record<string, UniformValue>, locationCache: Map<string, WebGLUniformLocation | null>): void;
7
+ export declare function setUniforms(gl: WebGLContext, program: WebGLProgram, uniforms: Record<string, UniformValue>, locationCache: Map<string, WebGLUniformLocation | null>, textureManager?: TextureManager): void;
7
8
  export declare function createUniformLocationCache(): Map<string, WebGLUniformLocation | null>;
8
9
  export declare function generateUniformDeclarations(uniforms: Record<string, UniformValue>): string;
9
10
  export declare function injectUniformDeclarations(shaderSource: string, customUniforms: Record<string, UniformValue> | undefined, defaultUniforms: Record<string, UniformValue>): string;
@@ -1 +1 @@
1
- {"version":3,"file":"uniforms.d.ts","sourceRoot":"","sources":["../../src/utils/uniforms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,YAAY,EAAqD,MAAM,UAAU,CAAA;AAE3G,KAAK,YAAY,GAAG,qBAAqB,GAAG,sBAAsB,CAAA;AAElE,eAAO,MAAM,gBAAgB,MAAM,CAAA;AAkCnC,wBAAgB,UAAU,CAAC,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE,oBAAoB,GAAG,IAAI,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,CAsB7G;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAErH;AAED,wBAAgB,WAAW,CACzB,EAAE,EAAE,YAAY,EAChB,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,EACtC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC,GACtD,IAAI,CAsBN;AAED,wBAAgB,0BAA0B,IAAI,GAAG,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC,CAErF;AA8BD,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,MAAM,CAa1F;AAID,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,SAAS,EACxD,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAC5C,MAAM,CAQR"}
1
+ {"version":3,"file":"uniforms.d.ts","sourceRoot":"","sources":["../../src/utils/uniforms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,YAAY,EAAqD,MAAM,UAAU,CAAA;AAC3G,OAAO,EAAiC,KAAK,cAAc,EAAE,MAAM,YAAY,CAAA;AAE/E,KAAK,YAAY,GAAG,qBAAqB,GAAG,sBAAsB,CAAA;AAElE,eAAO,MAAM,gBAAgB,MAAM,CAAA;AAkCnC,wBAAgB,UAAU,CAAC,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE,oBAAoB,GAAG,IAAI,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,CAsB7G;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAErH;AAED,wBAAgB,WAAW,CACzB,EAAE,EAAE,YAAY,EAChB,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,EACtC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC,EACvD,cAAc,CAAC,EAAE,cAAc,GAC9B,IAAI,CA8BN;AAED,wBAAgB,0BAA0B,IAAI,GAAG,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC,CAErF;AAiCD,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,MAAM,CAa1F;AAID,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,SAAS,EACxD,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAC5C,MAAM,CAQR"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fjandin/react-shader",
3
- "version": "0.0.12",
3
+ "version": "0.0.14",
4
4
  "description": "React component for rendering WebGL shaders",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",