@stream-io/video-filters-web 0.8.6 → 0.8.7

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.
Files changed (51) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/esm/index.js +10 -0
  3. package/dist/esm/index.js.map +1 -0
  4. package/dist/esm/src/BaseVideoProcessor.js +107 -0
  5. package/dist/esm/src/BaseVideoProcessor.js.map +1 -0
  6. package/dist/esm/src/FallbackGenerator.js +58 -0
  7. package/dist/esm/src/FallbackGenerator.js.map +1 -0
  8. package/dist/esm/src/FallbackProcessor.js +89 -0
  9. package/dist/esm/src/FallbackProcessor.js.map +1 -0
  10. package/dist/esm/src/FullScreenBlur.js +41 -0
  11. package/dist/esm/src/FullScreenBlur.js.map +1 -0
  12. package/dist/esm/src/FullScreenBlurRenderer.js +276 -0
  13. package/dist/esm/src/FullScreenBlurRenderer.js.map +1 -0
  14. package/dist/esm/src/VirtualBackground.js +143 -0
  15. package/dist/esm/src/VirtualBackground.js.map +1 -0
  16. package/dist/esm/src/WebGLRenderer.js +723 -0
  17. package/dist/esm/src/WebGLRenderer.js.map +1 -0
  18. package/dist/esm/src/compatibility.js +48 -0
  19. package/dist/esm/src/compatibility.js.map +1 -0
  20. package/dist/esm/src/legacy/createRenderer.js +33 -0
  21. package/dist/esm/src/legacy/createRenderer.js.map +1 -0
  22. package/dist/esm/src/legacy/helpers/webglHelper.js +94 -0
  23. package/dist/esm/src/legacy/helpers/webglHelper.js.map +1 -0
  24. package/dist/esm/src/legacy/segmentation.js +14 -0
  25. package/dist/esm/src/legacy/segmentation.js.map +1 -0
  26. package/dist/esm/src/legacy/tflite-simd.js +728 -0
  27. package/dist/esm/src/legacy/tflite-simd.js.map +1 -0
  28. package/dist/esm/src/legacy/tflite.js +31 -0
  29. package/dist/esm/src/legacy/tflite.js.map +1 -0
  30. package/dist/esm/src/legacy/webgl2/backgroundBlurStage.js +202 -0
  31. package/dist/esm/src/legacy/webgl2/backgroundBlurStage.js.map +1 -0
  32. package/dist/esm/src/legacy/webgl2/backgroundImageStage.js +157 -0
  33. package/dist/esm/src/legacy/webgl2/backgroundImageStage.js.map +1 -0
  34. package/dist/esm/src/legacy/webgl2/jointBilateralFilterStage.js +113 -0
  35. package/dist/esm/src/legacy/webgl2/jointBilateralFilterStage.js.map +1 -0
  36. package/dist/esm/src/legacy/webgl2/resizingStage.js +57 -0
  37. package/dist/esm/src/legacy/webgl2/resizingStage.js.map +1 -0
  38. package/dist/esm/src/legacy/webgl2/softmaxStage.js +51 -0
  39. package/dist/esm/src/legacy/webgl2/softmaxStage.js.map +1 -0
  40. package/dist/esm/src/legacy/webgl2/webgl2Pipeline.js +105 -0
  41. package/dist/esm/src/legacy/webgl2/webgl2Pipeline.js.map +1 -0
  42. package/dist/esm/src/mediapipe.js +16 -0
  43. package/dist/esm/src/mediapipe.js.map +1 -0
  44. package/dist/esm/src/types.js +11 -0
  45. package/dist/esm/src/types.js.map +1 -0
  46. package/dist/esm/src/version.js +5 -0
  47. package/dist/esm/src/version.js.map +1 -0
  48. package/dist/index.cjs.js +1 -1
  49. package/package.json +3 -3
  50. package/dist/index.es.js +0 -3022
  51. package/dist/index.es.js.map +0 -1
@@ -0,0 +1,723 @@
1
+ class WebGLRenderer {
2
+ canvas;
3
+ gl;
4
+ stateUpdateProgram;
5
+ maskRefineProgram;
6
+ blurProgram;
7
+ blendProgram;
8
+ stateUpdateLocations;
9
+ maskRefineLocations;
10
+ blurLocations;
11
+ blendLocations;
12
+ positionBuffer;
13
+ texCoordBuffer;
14
+ storedStateTextures;
15
+ fbo;
16
+ refineFbo;
17
+ refinedMaskTexture;
18
+ frameTexture;
19
+ blurTexture1;
20
+ blurTexture2;
21
+ blurFbo1;
22
+ blurFbo2;
23
+ running = false;
24
+ static DEFAULT_BG_COLOR = [33, 150, 243, 255];
25
+ currentStateIndex = 0;
26
+ backgroundRenderInfo = null;
27
+ activeBackgroundSourceIdentifier = null;
28
+ constructor(canvas) {
29
+ this.canvas = canvas;
30
+ const gl = this.canvas.getContext('webgl2', {
31
+ alpha: false,
32
+ antialias: false,
33
+ desynchronized: true,
34
+ });
35
+ if (!gl)
36
+ throw new Error('WebGL2 not supported');
37
+ this.gl = gl;
38
+ const stateUpdateVertexShaderSource = `attribute vec2 a_position; attribute vec2 a_texCoord; varying vec2 v_texCoord; void main() { gl_Position = vec4(a_position, 0.0, 1.0); v_texCoord = a_texCoord; }`;
39
+ const stateUpdateFragmentShaderSource = `
40
+ precision mediump float;
41
+ varying vec2 v_texCoord;
42
+ uniform sampler2D u_categoryTexture;
43
+ uniform sampler2D u_confidenceTexture;
44
+ uniform sampler2D u_prevStateTexture;
45
+ uniform float u_smoothingFactor;
46
+ uniform float u_smoothstepMin;
47
+ uniform float u_smoothstepMax;
48
+ uniform int u_selfieModel;
49
+
50
+ void main() {
51
+ vec2 prevCoord = vec2(v_texCoord.x, 1.0 - v_texCoord.y);
52
+ float categoryValue = texture2D(u_categoryTexture, v_texCoord).r;
53
+ float confidenceValue = texture2D(u_confidenceTexture, v_texCoord).r;
54
+
55
+ if (u_selfieModel == 1) {
56
+ categoryValue = 1.0 - categoryValue;
57
+ confidenceValue = 1.0 - confidenceValue;
58
+ }
59
+
60
+ if (categoryValue > 0.0) {
61
+ categoryValue = 1.0;
62
+ confidenceValue = 1.0 - confidenceValue;
63
+ }
64
+
65
+ float nonLinearConfidence = smoothstep(u_smoothstepMin, u_smoothstepMax, confidenceValue);
66
+ float prevCategoryValue = texture2D(u_prevStateTexture, prevCoord).r;
67
+ float alpha = u_smoothingFactor * nonLinearConfidence;
68
+ float newCategoryValue = alpha * categoryValue + (1.0 - alpha) * prevCategoryValue;
69
+
70
+ gl_FragColor = vec4(newCategoryValue, 0.0, 0.0, 0.0);
71
+ }
72
+ `;
73
+ this.stateUpdateProgram = this.createAndLinkProgram(stateUpdateVertexShaderSource, stateUpdateFragmentShaderSource);
74
+ this.stateUpdateLocations = {
75
+ position: gl.getAttribLocation(this.stateUpdateProgram, 'a_position'),
76
+ texCoord: gl.getAttribLocation(this.stateUpdateProgram, 'a_texCoord'),
77
+ categoryTexture: gl.getUniformLocation(this.stateUpdateProgram, 'u_categoryTexture'),
78
+ confidenceTexture: gl.getUniformLocation(this.stateUpdateProgram, 'u_confidenceTexture'),
79
+ prevStateTexture: gl.getUniformLocation(this.stateUpdateProgram, 'u_prevStateTexture'),
80
+ smoothingFactor: gl.getUniformLocation(this.stateUpdateProgram, 'u_smoothingFactor'),
81
+ smoothstepMin: gl.getUniformLocation(this.stateUpdateProgram, 'u_smoothstepMin'),
82
+ smoothstepMax: gl.getUniformLocation(this.stateUpdateProgram, 'u_smoothstepMax'),
83
+ selfieModel: gl.getUniformLocation(this.stateUpdateProgram, 'u_selfieModel'),
84
+ };
85
+ const maskRefineVertexShaderSource = stateUpdateVertexShaderSource;
86
+ const maskRefineFragmentShaderSource = `
87
+ precision mediump float;
88
+ varying vec2 v_texCoord;
89
+
90
+ uniform sampler2D u_maskTexture;
91
+ uniform sampler2D u_frameTexture;
92
+ uniform vec2 u_texelSize;
93
+ uniform float u_sigmaSpatial;
94
+ uniform float u_sigmaRange;
95
+
96
+ void main() {
97
+ vec2 flippedCoord = v_texCoord;
98
+ vec3 centerPixelColor = texture2D(u_frameTexture, v_texCoord).rgb;
99
+ float totalWeight = 0.0;
100
+ float weightedMaskSum = 0.0;
101
+
102
+ for (int offsetX = -2; offsetX <= 2; offsetX++) {
103
+ for (int offsetY = -2; offsetY <= 2; offsetY++) {
104
+ vec2 shift = vec2(float(offsetX), float(offsetY)) * u_texelSize;
105
+ vec2 frameCoord = v_texCoord + shift;
106
+ vec2 maskCoord = flippedCoord + shift;
107
+
108
+ vec3 neighborPixelColor = texture2D(u_frameTexture, frameCoord).rgb;
109
+ float neighborMaskValue = texture2D(u_maskTexture, maskCoord).r;
110
+
111
+ float spatialWeight = exp(-dot(shift, shift) / (2.0 * u_sigmaSpatial * u_sigmaSpatial));
112
+ vec3 colorDifference = neighborPixelColor - centerPixelColor;
113
+ float rangeWeight = exp(-(dot(colorDifference, colorDifference)) / (2.0 * u_sigmaRange * u_sigmaRange));
114
+
115
+ float combinedWeight = spatialWeight * rangeWeight;
116
+ weightedMaskSum += neighborMaskValue * combinedWeight;
117
+ totalWeight += combinedWeight;
118
+ }
119
+ }
120
+
121
+ float refinedMaskValue = weightedMaskSum / max(totalWeight, 1e-6);
122
+ gl_FragColor = vec4(refinedMaskValue, refinedMaskValue, refinedMaskValue, 1.0);
123
+ }
124
+ `;
125
+ this.maskRefineProgram = this.createAndLinkProgram(maskRefineVertexShaderSource, maskRefineFragmentShaderSource);
126
+ this.maskRefineLocations = {
127
+ position: gl.getAttribLocation(this.maskRefineProgram, 'a_position'),
128
+ texCoord: gl.getAttribLocation(this.maskRefineProgram, 'a_texCoord'),
129
+ maskTexture: gl.getUniformLocation(this.maskRefineProgram, 'u_maskTexture'),
130
+ frameTexture: gl.getUniformLocation(this.maskRefineProgram, 'u_frameTexture'),
131
+ texelSize: gl.getUniformLocation(this.maskRefineProgram, 'u_texelSize'),
132
+ sigmaSpatial: gl.getUniformLocation(this.maskRefineProgram, 'u_sigmaSpatial'),
133
+ sigmaRange: gl.getUniformLocation(this.maskRefineProgram, 'u_sigmaRange'),
134
+ };
135
+ const blurVertexShaderSource = stateUpdateVertexShaderSource;
136
+ const blurFragmentShaderSource = `
137
+ precision highp float;
138
+ varying vec2 v_texCoord;
139
+
140
+ uniform sampler2D u_image;
141
+ uniform sampler2D u_personMask;
142
+ uniform vec2 u_texelSize;
143
+ uniform float u_sigma;
144
+ uniform float u_radiusScale;
145
+ uniform vec2 u_direction;
146
+
147
+ const int KERNEL_RADIUS = 10;
148
+
149
+ float gauss(float x, float s) {
150
+ return exp(-(x * x) / (2.0 * s * s));
151
+ }
152
+
153
+ void main() {
154
+ vec2 maskCoord = u_direction.y > 0.5 ? vec2(v_texCoord.x, 1.0 - v_texCoord.y) : v_texCoord;
155
+ float mCenter = texture2D(u_personMask, maskCoord).r;
156
+ float wCenter = gauss(0.0, u_sigma);
157
+ vec4 accum = texture2D(u_image, v_texCoord) * wCenter * (1.0 - mCenter);
158
+ float weightSum = wCenter * (1.0 - mCenter);
159
+
160
+ for (int i = 1; i <= KERNEL_RADIUS; i++) {
161
+ float f = float(i);
162
+ float offset = f * u_radiusScale;
163
+ float w = gauss(offset, u_sigma);
164
+ vec2 texOffset = u_direction * offset * u_texelSize;
165
+
166
+ vec2 uvPlus = v_texCoord + texOffset;
167
+ vec2 maskCoordPlus = u_direction.y > 0.5 ? vec2(uvPlus.x, 1.0 - uvPlus.y) : uvPlus;
168
+ float mPlus = texture2D(u_personMask, maskCoordPlus).r;
169
+ accum += texture2D(u_image, uvPlus) * w * (1.0 - mPlus);
170
+ weightSum += w * (1.0 - mPlus);
171
+
172
+ vec2 uvMinus = v_texCoord - texOffset;
173
+ vec2 maskCoordMinus = u_direction.y > 0.5 ? vec2(uvMinus.x, 1.0 - uvMinus.y) : uvMinus;
174
+ float mMinus = texture2D(u_personMask, maskCoordMinus).r;
175
+ accum += texture2D(u_image, uvMinus) * w * (1.0 - mMinus);
176
+ weightSum += w * (1.0 - mMinus);
177
+ }
178
+
179
+ vec4 blurred = accum / max(weightSum, 1e-6);
180
+ gl_FragColor = blurred;
181
+ }
182
+ `;
183
+ this.blurProgram = this.createAndLinkProgram(blurVertexShaderSource, blurFragmentShaderSource);
184
+ this.blurLocations = {
185
+ position: gl.getAttribLocation(this.blurProgram, 'a_position'),
186
+ texCoord: gl.getAttribLocation(this.blurProgram, 'a_texCoord'),
187
+ image: gl.getUniformLocation(this.blurProgram, 'u_image'),
188
+ personMask: gl.getUniformLocation(this.blurProgram, 'u_personMask'),
189
+ texelSize: gl.getUniformLocation(this.blurProgram, 'u_texelSize'),
190
+ sigma: gl.getUniformLocation(this.blurProgram, 'u_sigma'),
191
+ radiusScale: gl.getUniformLocation(this.blurProgram, 'u_radiusScale'),
192
+ direction: gl.getUniformLocation(this.blurProgram, 'u_direction'),
193
+ };
194
+ const blendVertexShaderSource = stateUpdateVertexShaderSource;
195
+ const blendFragmentShaderSource = `
196
+ precision mediump float;
197
+ varying vec2 v_texCoord;
198
+
199
+ uniform sampler2D u_frameTexture;
200
+ uniform sampler2D u_currentStateTexture;
201
+ uniform sampler2D u_backgroundTexture;
202
+ uniform vec2 u_bgImageDimensions;
203
+ uniform vec2 u_canvasDimensions;
204
+ uniform float u_borderSmooth;
205
+ uniform float u_bgBlur;
206
+ uniform float u_bgBlurRadius;
207
+ uniform int u_enabled;
208
+
209
+ vec4 getMixedFragColor(vec2 bgTexCoord, vec2 categoryCoord, vec2 offset) {
210
+ vec4 backgroundColor = texture2D(u_backgroundTexture, bgTexCoord + offset);
211
+ vec4 frameColor = texture2D(u_frameTexture, v_texCoord + offset);
212
+ float categoryValue = texture2D(u_currentStateTexture, categoryCoord + offset).r;
213
+ return mix(backgroundColor, frameColor, categoryValue);
214
+ }
215
+
216
+ void main() {
217
+ if (u_enabled == 0) {
218
+ gl_FragColor = texture2D(u_frameTexture, v_texCoord);
219
+ return;
220
+ }
221
+
222
+ vec2 categoryCoord = v_texCoord;
223
+ float categoryValue = texture2D(u_currentStateTexture, categoryCoord).r;
224
+
225
+ float canvasAspect = u_canvasDimensions.x / u_canvasDimensions.y;
226
+ float bgAspect = u_bgImageDimensions.x / u_bgImageDimensions.y;
227
+
228
+ vec2 bgTexCoord = v_texCoord;
229
+ float scaleX = 1.0;
230
+ float scaleY = 1.0;
231
+ float offsetX = 0.0;
232
+ float offsetY = 0.0;
233
+
234
+ if (canvasAspect < bgAspect) {
235
+ scaleY = 1.0;
236
+ scaleX = bgAspect / canvasAspect;
237
+ offsetX = (1.0 - scaleX) / 2.0;
238
+ } else {
239
+ scaleX = 1.0;
240
+ scaleY = canvasAspect / bgAspect;
241
+ offsetY = (1.0 - scaleY) / 2.0;
242
+ }
243
+
244
+ bgTexCoord = vec2((v_texCoord.x - offsetX) / scaleX, (v_texCoord.y - offsetY) / scaleY);
245
+ gl_FragColor = getMixedFragColor(bgTexCoord, categoryCoord, vec2(0.0, 0.0));
246
+ }`;
247
+ this.blendProgram = this.createAndLinkProgram(blendVertexShaderSource, blendFragmentShaderSource);
248
+ this.blendLocations = {
249
+ position: gl.getAttribLocation(this.blendProgram, 'a_position'),
250
+ texCoord: gl.getAttribLocation(this.blendProgram, 'a_texCoord'),
251
+ frameTexture: gl.getUniformLocation(this.blendProgram, 'u_frameTexture'),
252
+ currentStateTexture: gl.getUniformLocation(this.blendProgram, 'u_currentStateTexture'),
253
+ backgroundTexture: gl.getUniformLocation(this.blendProgram, 'u_backgroundTexture'),
254
+ bgImageDimensions: gl.getUniformLocation(this.blendProgram, 'u_bgImageDimensions'),
255
+ canvasDimensions: gl.getUniformLocation(this.blendProgram, 'u_canvasDimensions'),
256
+ borderSmooth: gl.getUniformLocation(this.blendProgram, 'u_borderSmooth'),
257
+ bgBlur: gl.getUniformLocation(this.blendProgram, 'u_bgBlur'),
258
+ bgBlurRadius: gl.getUniformLocation(this.blendProgram, 'u_bgBlurRadius'),
259
+ enabled: gl.getUniformLocation(this.blendProgram, 'u_enabled'),
260
+ };
261
+ this.positionBuffer = gl.createBuffer();
262
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
263
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]), gl.STATIC_DRAW);
264
+ this.texCoordBuffer = gl.createBuffer();
265
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);
266
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0]), gl.STATIC_DRAW);
267
+ this.storedStateTextures = Array.from({ length: 2 }, () => {
268
+ const tex = gl.createTexture();
269
+ gl.bindTexture(gl.TEXTURE_2D, tex);
270
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 0, 255]));
271
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
272
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
273
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
274
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
275
+ return tex;
276
+ });
277
+ gl.bindTexture(gl.TEXTURE_2D, null);
278
+ this.fbo = gl.createFramebuffer();
279
+ this.refineFbo = gl.createFramebuffer();
280
+ const refinedTex = gl.createTexture();
281
+ this.frameTexture = gl.createTexture();
282
+ if (!refinedTex)
283
+ throw new Error('Failed to create refined mask texture');
284
+ gl.bindTexture(gl.TEXTURE_2D, refinedTex);
285
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
286
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
287
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
288
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
289
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
290
+ gl.bindTexture(gl.TEXTURE_2D, null);
291
+ this.refinedMaskTexture = refinedTex;
292
+ const mkColorTex = () => {
293
+ const t = gl.createTexture();
294
+ if (!t)
295
+ throw new Error('Failed to create blur texture');
296
+ gl.bindTexture(gl.TEXTURE_2D, t);
297
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
298
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
299
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
300
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
301
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
302
+ gl.bindTexture(gl.TEXTURE_2D, null);
303
+ return t;
304
+ };
305
+ this.blurTexture1 = mkColorTex();
306
+ this.blurTexture2 = mkColorTex();
307
+ const mkFbo = (tex) => {
308
+ const fb = gl.createFramebuffer();
309
+ if (!fb || !tex)
310
+ throw new Error('Failed to create blur FBO');
311
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
312
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
313
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
314
+ return fb;
315
+ };
316
+ this.blurFbo1 = mkFbo(this.blurTexture1);
317
+ this.blurFbo2 = mkFbo(this.blurTexture2);
318
+ this.running = true;
319
+ }
320
+ createAndLinkProgram(vsSource, fsSource) {
321
+ const vs = this.createShader(this.gl.VERTEX_SHADER, vsSource);
322
+ const fs = this.createShader(this.gl.FRAGMENT_SHADER, fsSource);
323
+ const prog = this.gl.createProgram();
324
+ if (!prog)
325
+ throw new Error('Failed to create program');
326
+ this.gl.attachShader(prog, vs);
327
+ this.gl.attachShader(prog, fs);
328
+ this.gl.linkProgram(prog);
329
+ if (!this.gl.getProgramParameter(prog, this.gl.LINK_STATUS)) {
330
+ console.error('Program link error:', this.gl.getProgramInfoLog(prog));
331
+ this.gl.deleteProgram(prog);
332
+ throw new Error('Link fail');
333
+ }
334
+ this.gl.detachShader(prog, vs);
335
+ this.gl.detachShader(prog, fs);
336
+ this.gl.deleteShader(vs);
337
+ this.gl.deleteShader(fs);
338
+ return prog;
339
+ }
340
+ createShader(type, source) {
341
+ const shader = this.gl.createShader(type);
342
+ if (!shader)
343
+ throw new Error(`Failed to create shader type: ${type}`);
344
+ this.gl.shaderSource(shader, source);
345
+ this.gl.compileShader(shader);
346
+ if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
347
+ console.error('Shader compile error:', this.gl.getShaderInfoLog(shader));
348
+ this.gl.deleteShader(shader);
349
+ throw new Error('Failed to compile shader');
350
+ }
351
+ return shader;
352
+ }
353
+ createColorTexture(r, g, b, a) {
354
+ const texture = this.gl.createTexture();
355
+ if (!texture)
356
+ throw new Error('Failed to create texture for color');
357
+ this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
358
+ const pixel = new Uint8Array([r, g, b, a]);
359
+ this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, 1, 1, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, pixel);
360
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
361
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
362
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST);
363
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST);
364
+ this.gl.bindTexture(this.gl.TEXTURE_2D, null);
365
+ return { texture, color: [r, g, b, a] };
366
+ }
367
+ updateBackgroundIfNeeded(newSource) {
368
+ const gl = this.gl;
369
+ let newIdentifier;
370
+ if (!newSource) {
371
+ const [r, g, b, a] = WebGLRenderer.DEFAULT_BG_COLOR;
372
+ newIdentifier = `color(${r},${g},${b},${a})`;
373
+ }
374
+ else {
375
+ newIdentifier = newSource.url;
376
+ }
377
+ if (newIdentifier === this.activeBackgroundSourceIdentifier &&
378
+ this.backgroundRenderInfo) {
379
+ return;
380
+ }
381
+ if (this.backgroundRenderInfo) {
382
+ gl.deleteTexture(this.backgroundRenderInfo.texture);
383
+ this.backgroundRenderInfo = null;
384
+ }
385
+ this.activeBackgroundSourceIdentifier = newIdentifier;
386
+ if (!newSource) {
387
+ const [r, g, b, a] = WebGLRenderer.DEFAULT_BG_COLOR;
388
+ const colorTexData = this.createColorTexture(r, g, b, a);
389
+ this.backgroundRenderInfo = {
390
+ type: 'color',
391
+ texture: colorTexData.texture,
392
+ color: colorTexData.color,
393
+ };
394
+ this.activeBackgroundSourceIdentifier = `color(${r},${g},${b},${a})`;
395
+ }
396
+ else {
397
+ if (newSource.type === 'image') {
398
+ const { media, url } = newSource;
399
+ const texture = this.gl.createTexture();
400
+ if (!texture) {
401
+ throw new Error('Failed to create texture object for image.');
402
+ }
403
+ this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
404
+ this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, media);
405
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
406
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
407
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
408
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
409
+ this.gl.bindTexture(this.gl.TEXTURE_2D, null);
410
+ this.backgroundRenderInfo = {
411
+ type: 'image',
412
+ texture,
413
+ width: media.width,
414
+ height: media.height,
415
+ url,
416
+ };
417
+ }
418
+ else if (newSource.type === 'video') {
419
+ const { media, url } = newSource;
420
+ const canvas = new OffscreenCanvas(1, 1);
421
+ const ctx = canvas.getContext('2d');
422
+ const writer = new WritableStream({
423
+ write(videoFrame) {
424
+ canvas.width = videoFrame.codedWidth;
425
+ canvas.height = videoFrame.codedHeight;
426
+ ctx?.drawImage(videoFrame, 0, 0);
427
+ videoFrame.close();
428
+ },
429
+ close() {
430
+ console.log('[virtual-background] video background close');
431
+ },
432
+ });
433
+ media.pipeTo(writer).catch((err) => {
434
+ console.error('media.pipeTo(writer) error', err);
435
+ });
436
+ const texture = this.gl.createTexture();
437
+ if (!texture)
438
+ throw new Error('Failed to create texture for video');
439
+ this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
440
+ this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, 1, 1, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, null);
441
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
442
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
443
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
444
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
445
+ this.gl.bindTexture(this.gl.TEXTURE_2D, null);
446
+ this.backgroundRenderInfo = {
447
+ type: 'video',
448
+ texture,
449
+ url,
450
+ media,
451
+ canvas,
452
+ };
453
+ }
454
+ }
455
+ if (!this.backgroundRenderInfo) {
456
+ console.error('Critical: backgroundRenderInfo is null after processing new source. Setting default color.');
457
+ const [r, g, b, a] = WebGLRenderer.DEFAULT_BG_COLOR;
458
+ const colorTexData = this.createColorTexture(r, g, b, a);
459
+ this.backgroundRenderInfo = {
460
+ type: 'color',
461
+ texture: colorTexData.texture,
462
+ color: colorTexData.color,
463
+ };
464
+ this.activeBackgroundSourceIdentifier = `color(${r},${g},${b},${a})`;
465
+ }
466
+ }
467
+ render(videoFrame, options, categoryTexture, confidenceTexture) {
468
+ if (!this.running)
469
+ return;
470
+ const { gl, fbo, frameTexture, storedStateTextures, stateUpdateProgram, stateUpdateLocations, refineFbo, refinedMaskTexture, maskRefineProgram, maskRefineLocations, blendProgram, blendLocations, blurFbo1, blurFbo2, blurTexture1, blurTexture2, } = this;
471
+ const { displayWidth: width, displayHeight: height } = videoFrame;
472
+ if (this.canvas.width !== width || this.canvas.height !== height) {
473
+ this.canvas.width = width;
474
+ this.canvas.height = height;
475
+ }
476
+ if (!categoryTexture || !confidenceTexture) {
477
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
478
+ gl.useProgram(blendProgram);
479
+ const frame = gl.createTexture();
480
+ gl.activeTexture(gl.TEXTURE0);
481
+ gl.bindTexture(gl.TEXTURE_2D, frame);
482
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, videoFrame);
483
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
484
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
485
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
486
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
487
+ gl.uniform1i(blendLocations.frameTexture, 0);
488
+ gl.uniform1i(blendLocations.enabled, 0);
489
+ gl.enableVertexAttribArray(blendLocations.position);
490
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
491
+ gl.vertexAttribPointer(blendLocations.position, 2, gl.FLOAT, false, 0, 0);
492
+ gl.enableVertexAttribArray(blendLocations.texCoord);
493
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);
494
+ gl.vertexAttribPointer(blendLocations.texCoord, 2, gl.FLOAT, false, 0, 0);
495
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
496
+ gl.deleteTexture(frame);
497
+ gl.activeTexture(gl.TEXTURE0);
498
+ gl.bindTexture(gl.TEXTURE_2D, null);
499
+ return;
500
+ }
501
+ const readStateIndex = this.currentStateIndex;
502
+ const writeStateIndex = (this.currentStateIndex + 1) % 2;
503
+ const prevStateTexture = storedStateTextures[readStateIndex];
504
+ const newStateTexture = storedStateTextures[writeStateIndex];
505
+ this.updateBackgroundIfNeeded(options.backgroundSource);
506
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
507
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, newStateTexture, 0);
508
+ gl.bindTexture(gl.TEXTURE_2D, newStateTexture);
509
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
510
+ gl.viewport(0, 0, width, height);
511
+ gl.useProgram(stateUpdateProgram);
512
+ gl.activeTexture(gl.TEXTURE0);
513
+ gl.bindTexture(gl.TEXTURE_2D, categoryTexture);
514
+ gl.uniform1i(stateUpdateLocations.categoryTexture, 0);
515
+ gl.activeTexture(gl.TEXTURE1);
516
+ gl.bindTexture(gl.TEXTURE_2D, confidenceTexture);
517
+ gl.uniform1i(stateUpdateLocations.confidenceTexture, 1);
518
+ gl.activeTexture(gl.TEXTURE2);
519
+ gl.bindTexture(gl.TEXTURE_2D, prevStateTexture);
520
+ gl.uniform1i(stateUpdateLocations.prevStateTexture, 2);
521
+ gl.uniform1f(stateUpdateLocations.smoothingFactor, options.segmentationOptions?.smoothingFactor ?? 0.8);
522
+ gl.uniform1f(stateUpdateLocations.smoothstepMin, options.segmentationOptions?.smoothstepMin ?? 0.6);
523
+ gl.uniform1f(stateUpdateLocations.smoothstepMax, options.segmentationOptions?.smoothstepMax ?? 0.9);
524
+ gl.uniform1i(stateUpdateLocations.selfieModel, options.isSelfieMode ? 1 : 0);
525
+ gl.enableVertexAttribArray(stateUpdateLocations.position);
526
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
527
+ gl.vertexAttribPointer(stateUpdateLocations.position, 2, gl.FLOAT, false, 0, 0);
528
+ gl.enableVertexAttribArray(stateUpdateLocations.texCoord);
529
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);
530
+ gl.vertexAttribPointer(stateUpdateLocations.texCoord, 2, gl.FLOAT, false, 0, 0);
531
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
532
+ gl.bindFramebuffer(gl.FRAMEBUFFER, refineFbo);
533
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, refinedMaskTexture, 0);
534
+ gl.bindTexture(gl.TEXTURE_2D, refinedMaskTexture);
535
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
536
+ gl.viewport(0, 0, width, height);
537
+ gl.useProgram(maskRefineProgram);
538
+ gl.enableVertexAttribArray(maskRefineLocations.position);
539
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
540
+ gl.vertexAttribPointer(maskRefineLocations.position, 2, gl.FLOAT, false, 0, 0);
541
+ gl.enableVertexAttribArray(maskRefineLocations.texCoord);
542
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);
543
+ gl.vertexAttribPointer(maskRefineLocations.texCoord, 2, gl.FLOAT, false, 0, 0);
544
+ gl.activeTexture(gl.TEXTURE0);
545
+ gl.bindTexture(gl.TEXTURE_2D, newStateTexture);
546
+ gl.uniform1i(maskRefineLocations.maskTexture, 0);
547
+ gl.activeTexture(gl.TEXTURE1);
548
+ gl.bindTexture(gl.TEXTURE_2D, frameTexture);
549
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, videoFrame);
550
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
551
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
552
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
553
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
554
+ gl.uniform1i(maskRefineLocations.frameTexture, 1);
555
+ gl.uniform2f(maskRefineLocations.texelSize, 1.0 / width, 1.0 / height);
556
+ gl.uniform1f(maskRefineLocations.sigmaSpatial, 2.0);
557
+ gl.uniform1f(maskRefineLocations.sigmaRange, 0.1);
558
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
559
+ gl.disableVertexAttribArray(maskRefineLocations.position);
560
+ gl.disableVertexAttribArray(maskRefineLocations.texCoord);
561
+ let backgroundTexToUse;
562
+ let bgWToSend = width;
563
+ let bgHToSend = height;
564
+ if (options.bgBlur > 0 && options.bgBlurRadius > 0) {
565
+ const downscale = 0.5;
566
+ const blurW = Math.floor(width * downscale);
567
+ const blurH = Math.floor(height * downscale);
568
+ gl.bindTexture(gl.TEXTURE_2D, blurTexture1);
569
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, blurW, blurH, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
570
+ gl.bindTexture(gl.TEXTURE_2D, blurTexture2);
571
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, blurW, blurH, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
572
+ const KERNEL_RADIUS = 10.0;
573
+ const radiusScale = Math.max(0.0, options.bgBlurRadius) / KERNEL_RADIUS;
574
+ gl.useProgram(this.blurProgram);
575
+ gl.enableVertexAttribArray(this.blurLocations.position);
576
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
577
+ gl.vertexAttribPointer(this.blurLocations.position, 2, gl.FLOAT, false, 0, 0);
578
+ gl.enableVertexAttribArray(this.blurLocations.texCoord);
579
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);
580
+ gl.vertexAttribPointer(this.blurLocations.texCoord, 2, gl.FLOAT, false, 0, 0);
581
+ gl.activeTexture(gl.TEXTURE1);
582
+ gl.bindTexture(gl.TEXTURE_2D, refinedMaskTexture);
583
+ gl.uniform1i(this.blurLocations.personMask, 1);
584
+ gl.uniform1f(this.blurLocations.sigma, options.bgBlur * 0.7);
585
+ gl.uniform1f(this.blurLocations.radiusScale, radiusScale);
586
+ const blurPasses = [
587
+ {
588
+ direction: [1.0, 0.0],
589
+ input: frameTexture,
590
+ output: blurFbo1,
591
+ texelSize: [1.0 / width, 1.0 / height],
592
+ },
593
+ {
594
+ direction: [0.0, 1.0],
595
+ input: blurTexture1,
596
+ output: blurFbo2,
597
+ texelSize: [1.0 / blurW, 1.0 / blurH],
598
+ },
599
+ {
600
+ direction: [1.0, 0.0],
601
+ input: blurTexture2,
602
+ output: blurFbo1,
603
+ texelSize: [1.0 / blurW, 1.0 / blurH],
604
+ },
605
+ {
606
+ direction: [0.0, 1.0],
607
+ input: blurTexture1,
608
+ output: blurFbo2,
609
+ texelSize: [1.0 / blurW, 1.0 / blurH],
610
+ },
611
+ ];
612
+ for (const pass of blurPasses) {
613
+ gl.bindFramebuffer(gl.FRAMEBUFFER, pass.output);
614
+ gl.viewport(0, 0, blurW, blurH);
615
+ gl.activeTexture(gl.TEXTURE0);
616
+ gl.bindTexture(gl.TEXTURE_2D, pass.input);
617
+ gl.uniform1i(this.blurLocations.image, 0);
618
+ gl.uniform2f(this.blurLocations.texelSize, pass.texelSize[0], pass.texelSize[1]);
619
+ gl.uniform2f(this.blurLocations.direction, pass.direction[0], pass.direction[1]);
620
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
621
+ }
622
+ backgroundTexToUse = blurTexture2;
623
+ bgWToSend = blurW;
624
+ bgHToSend = blurH;
625
+ }
626
+ else if (options.backgroundSource && this.backgroundRenderInfo) {
627
+ backgroundTexToUse = this.backgroundRenderInfo.texture;
628
+ if (this.backgroundRenderInfo.type === 'video') {
629
+ const { canvas } = this.backgroundRenderInfo;
630
+ bgWToSend = canvas.width || width;
631
+ bgHToSend = canvas.height || height;
632
+ }
633
+ else if (this.backgroundRenderInfo.type === 'image') {
634
+ bgWToSend = this.backgroundRenderInfo.width;
635
+ bgHToSend = this.backgroundRenderInfo.height;
636
+ }
637
+ else {
638
+ bgWToSend = width;
639
+ bgHToSend = height;
640
+ }
641
+ }
642
+ else {
643
+ backgroundTexToUse = this.backgroundRenderInfo?.texture ?? null;
644
+ }
645
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
646
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
647
+ gl.useProgram(blendProgram);
648
+ gl.activeTexture(gl.TEXTURE0);
649
+ gl.bindTexture(gl.TEXTURE_2D, frameTexture);
650
+ gl.uniform1i(blendLocations.frameTexture, 0);
651
+ gl.uniform1f(blendLocations.borderSmooth, 0);
652
+ gl.uniform1f(blendLocations.bgBlur, options.bgBlur);
653
+ gl.uniform1f(blendLocations.bgBlurRadius, options.bgBlurRadius);
654
+ gl.uniform1i(blendLocations.enabled, 1);
655
+ gl.activeTexture(gl.TEXTURE1);
656
+ gl.bindTexture(gl.TEXTURE_2D, refinedMaskTexture);
657
+ gl.uniform1i(blendLocations.currentStateTexture, 1);
658
+ if (backgroundTexToUse) {
659
+ gl.activeTexture(gl.TEXTURE2);
660
+ gl.bindTexture(gl.TEXTURE_2D, backgroundTexToUse);
661
+ gl.uniform1i(blendLocations.backgroundTexture, 2);
662
+ gl.uniform2f(blendLocations.bgImageDimensions, bgWToSend, bgHToSend);
663
+ gl.uniform2f(blendLocations.canvasDimensions, width, height);
664
+ }
665
+ else {
666
+ gl.uniform2f(blendLocations.bgImageDimensions, width, height);
667
+ gl.uniform2f(blendLocations.canvasDimensions, width, height);
668
+ }
669
+ gl.enableVertexAttribArray(blendLocations.position);
670
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
671
+ gl.vertexAttribPointer(blendLocations.position, 2, gl.FLOAT, false, 0, 0);
672
+ gl.enableVertexAttribArray(blendLocations.texCoord);
673
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);
674
+ gl.vertexAttribPointer(blendLocations.texCoord, 2, gl.FLOAT, false, 0, 0);
675
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
676
+ for (let i = 0; i < 3; ++i) {
677
+ gl.activeTexture(gl.TEXTURE0 + i);
678
+ gl.bindTexture(gl.TEXTURE_2D, null);
679
+ }
680
+ this.currentStateIndex = writeStateIndex;
681
+ }
682
+ close() {
683
+ if (!this.running)
684
+ return;
685
+ this.running = false;
686
+ const { gl, fbo, refineFbo, refinedMaskTexture, blurFbo1, blurFbo2 } = this;
687
+ gl.clearColor(0, 0, 0, 0);
688
+ gl.clear(gl.COLOR_BUFFER_BIT);
689
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
690
+ if (fbo)
691
+ gl.deleteFramebuffer(fbo);
692
+ if (refineFbo)
693
+ gl.deleteFramebuffer(refineFbo);
694
+ if (blurFbo1)
695
+ gl.deleteFramebuffer(blurFbo1);
696
+ if (blurFbo2)
697
+ gl.deleteFramebuffer(blurFbo2);
698
+ gl.deleteProgram(this.stateUpdateProgram);
699
+ gl.deleteProgram(this.maskRefineProgram);
700
+ gl.deleteProgram(this.blurProgram);
701
+ gl.deleteProgram(this.blendProgram);
702
+ if (this.positionBuffer)
703
+ gl.deleteBuffer(this.positionBuffer);
704
+ if (this.texCoordBuffer)
705
+ gl.deleteBuffer(this.texCoordBuffer);
706
+ if (refinedMaskTexture)
707
+ gl.deleteTexture(refinedMaskTexture);
708
+ if (this.blurTexture1)
709
+ gl.deleteTexture(this.blurTexture1);
710
+ if (this.blurTexture2)
711
+ gl.deleteTexture(this.blurTexture2);
712
+ this.storedStateTextures.forEach((t) => t && gl.deleteTexture(t));
713
+ this.storedStateTextures.splice(0, this.storedStateTextures.length);
714
+ if (this.backgroundRenderInfo?.texture) {
715
+ gl.deleteTexture(this.backgroundRenderInfo.texture);
716
+ this.backgroundRenderInfo = null;
717
+ }
718
+ this.activeBackgroundSourceIdentifier = null;
719
+ }
720
+ }
721
+
722
+ export { WebGLRenderer };
723
+ //# sourceMappingURL=WebGLRenderer.js.map