shaders-oneshot 0.0.81-shared.1
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/LICENSE +201 -0
- package/NOTICE +5 -0
- package/README.md +7 -0
- package/dist/empty-pixel.d.ts +1 -0
- package/dist/empty-pixel.js +10 -0
- package/dist/empty-pixel.js.map +7 -0
- package/dist/get-shader-color-from-string.d.ts +3 -0
- package/dist/get-shader-color-from-string.js +101 -0
- package/dist/get-shader-color-from-string.js.map +7 -0
- package/dist/get-shader-color-from-string.test.d.ts +1 -0
- package/dist/get-shader-color-from-string.test.js +69 -0
- package/dist/get-shader-noise-texture.d.ts +1 -0
- package/dist/get-shader-noise-texture.js +18 -0
- package/dist/get-shader-noise-texture.js.map +7 -0
- package/dist/index.d.ts +68 -0
- package/dist/index.js +194 -0
- package/dist/index.js.map +7 -0
- package/dist/shader-color-spaces.d.ts +6 -0
- package/dist/shader-color-spaces.js +96 -0
- package/dist/shader-color-spaces.js.map +7 -0
- package/dist/shader-mount.d.ts +199 -0
- package/dist/shader-mount.js +735 -0
- package/dist/shader-mount.js.map +7 -0
- package/dist/shader-sizing.d.ts +30 -0
- package/dist/shader-sizing.js +38 -0
- package/dist/shader-sizing.js.map +7 -0
- package/dist/shader-utils.d.ts +10 -0
- package/dist/shader-utils.js +131 -0
- package/dist/shader-utils.js.map +7 -0
- package/dist/shaders/color-panels.d.ts +70 -0
- package/dist/shaders/color-panels.js +224 -0
- package/dist/shaders/color-panels.js.map +7 -0
- package/dist/shaders/dithering.d.ts +62 -0
- package/dist/shaders/dithering.js +271 -0
- package/dist/shaders/dithering.js.map +7 -0
- package/dist/shaders/dot-grid.d.ts +65 -0
- package/dist/shaders/dot-grid.js +110 -0
- package/dist/shaders/dot-grid.js.map +7 -0
- package/dist/shaders/dot-orbit.d.ts +57 -0
- package/dist/shaders/dot-orbit.js +123 -0
- package/dist/shaders/dot-orbit.js.map +7 -0
- package/dist/shaders/fluted-glass.d.ts +112 -0
- package/dist/shaders/fluted-glass.js +361 -0
- package/dist/shaders/fluted-glass.js.map +7 -0
- package/dist/shaders/gem-smoke.d.ts +96 -0
- package/dist/shaders/gem-smoke.js +573 -0
- package/dist/shaders/gem-smoke.js.map +7 -0
- package/dist/shaders/god-rays.d.ts +67 -0
- package/dist/shaders/god-rays.js +133 -0
- package/dist/shaders/god-rays.js.map +7 -0
- package/dist/shaders/grain-gradient.d.ts +80 -0
- package/dist/shaders/grain-gradient.js +301 -0
- package/dist/shaders/grain-gradient.js.map +7 -0
- package/dist/shaders/halftone-cmyk.d.ts +104 -0
- package/dist/shaders/halftone-cmyk.js +295 -0
- package/dist/shaders/halftone-cmyk.js.map +7 -0
- package/dist/shaders/halftone-dots.d.ts +80 -0
- package/dist/shaders/halftone-dots.js +321 -0
- package/dist/shaders/halftone-dots.js.map +7 -0
- package/dist/shaders/heatmap.d.ts +66 -0
- package/dist/shaders/heatmap.js +384 -0
- package/dist/shaders/heatmap.js.map +7 -0
- package/dist/shaders/image-dithering.d.ts +64 -0
- package/dist/shaders/image-dithering.js +199 -0
- package/dist/shaders/image-dithering.js.map +7 -0
- package/dist/shaders/lens-distortion.d.ts +97 -0
- package/dist/shaders/lens-distortion.js +276 -0
- package/dist/shaders/lens-distortion.js.map +7 -0
- package/dist/shaders/liquid-metal.d.ts +89 -0
- package/dist/shaders/liquid-metal.js +633 -0
- package/dist/shaders/liquid-metal.js.map +7 -0
- package/dist/shaders/mesh-gradient.d.ts +52 -0
- package/dist/shaders/mesh-gradient.js +131 -0
- package/dist/shaders/mesh-gradient.js.map +7 -0
- package/dist/shaders/metaballs.d.ts +51 -0
- package/dist/shaders/metaballs.js +111 -0
- package/dist/shaders/metaballs.js.map +7 -0
- package/dist/shaders/neuro-noise.d.ts +47 -0
- package/dist/shaders/neuro-noise.js +77 -0
- package/dist/shaders/neuro-noise.js.map +7 -0
- package/dist/shaders/paper-texture.d.ts +109 -0
- package/dist/shaders/paper-texture.js +527 -0
- package/dist/shaders/paper-texture.js.map +7 -0
- package/dist/shaders/perlin-noise.d.ts +52 -0
- package/dist/shaders/perlin-noise.js +167 -0
- package/dist/shaders/perlin-noise.js.map +7 -0
- package/dist/shaders/pulsing-border.d.ts +98 -0
- package/dist/shaders/pulsing-border.js +239 -0
- package/dist/shaders/pulsing-border.js.map +7 -0
- package/dist/shaders/simplex-noise.d.ts +45 -0
- package/dist/shaders/simplex-noise.js +92 -0
- package/dist/shaders/simplex-noise.js.map +7 -0
- package/dist/shaders/smoke-ring.d.ts +61 -0
- package/dist/shaders/smoke-ring.js +133 -0
- package/dist/shaders/smoke-ring.js.map +7 -0
- package/dist/shaders/spiral.d.ts +61 -0
- package/dist/shaders/spiral.js +83 -0
- package/dist/shaders/spiral.js.map +7 -0
- package/dist/shaders/static-mesh-gradient.d.ts +63 -0
- package/dist/shaders/static-mesh-gradient.js +131 -0
- package/dist/shaders/static-mesh-gradient.js.map +7 -0
- package/dist/shaders/static-radial-gradient.d.ts +72 -0
- package/dist/shaders/static-radial-gradient.js +188 -0
- package/dist/shaders/static-radial-gradient.js.map +7 -0
- package/dist/shaders/swirl.d.ts +63 -0
- package/dist/shaders/swirl.js +104 -0
- package/dist/shaders/swirl.js.map +7 -0
- package/dist/shaders/voronoi.d.ts +65 -0
- package/dist/shaders/voronoi.js +139 -0
- package/dist/shaders/voronoi.js.map +7 -0
- package/dist/shaders/warp.d.ts +70 -0
- package/dist/shaders/warp.js +130 -0
- package/dist/shaders/warp.js.map +7 -0
- package/dist/shaders/water.d.ts +59 -0
- package/dist/shaders/water.js +122 -0
- package/dist/shaders/water.js.map +7 -0
- package/dist/shaders/waves.d.ts +53 -0
- package/dist/shaders/waves.js +66 -0
- package/dist/shaders/waves.js.map +7 -0
- package/dist/shared-webgl-renderer.d.ts +63 -0
- package/dist/shared-webgl-renderer.js +262 -0
- package/dist/shared-webgl-renderer.js.map +7 -0
- package/dist/types.d.ts +3 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +7 -0
- package/dist/vertex-shader.d.ts +2 -0
- package/dist/vertex-shader.js +160 -0
- package/dist/vertex-shader.js.map +7 -0
- package/package.json +29 -0
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
2
|
+
* Paper Shaders *
|
|
3
|
+
* https://github.com/paper-design/shaders *
|
|
4
|
+
* * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
5
|
+
|
|
6
|
+
import { rotation2, declarePI } from "../shader-utils.js";
|
|
7
|
+
const gemSmokeMeta = {
|
|
8
|
+
maxColorCount: 6
|
|
9
|
+
};
|
|
10
|
+
const gemSmokeFragmentShader = `#version 300 es
|
|
11
|
+
precision mediump float;
|
|
12
|
+
|
|
13
|
+
in mediump vec2 v_imageUV;
|
|
14
|
+
in mediump vec2 v_objectUV;
|
|
15
|
+
in mediump vec2 v_responsiveUV;
|
|
16
|
+
in mediump vec2 v_responsiveBoxGivenSize;
|
|
17
|
+
|
|
18
|
+
out vec4 fragColor;
|
|
19
|
+
|
|
20
|
+
uniform sampler2D u_image;
|
|
21
|
+
uniform float u_shape;
|
|
22
|
+
uniform bool u_isImage;
|
|
23
|
+
|
|
24
|
+
uniform float u_time;
|
|
25
|
+
uniform vec4 u_colors[${gemSmokeMeta.maxColorCount}];
|
|
26
|
+
uniform float u_colorsCount;
|
|
27
|
+
uniform vec4 u_colorBack;
|
|
28
|
+
uniform vec4 u_colorInner;
|
|
29
|
+
uniform float u_innerDistortion;
|
|
30
|
+
uniform float u_outerDistortion;
|
|
31
|
+
uniform float u_outerGlow;
|
|
32
|
+
uniform float u_innerGlow;
|
|
33
|
+
uniform float u_offset;
|
|
34
|
+
uniform float u_angle;
|
|
35
|
+
uniform float u_size;
|
|
36
|
+
|
|
37
|
+
${declarePI}
|
|
38
|
+
${rotation2}
|
|
39
|
+
|
|
40
|
+
// 9x9 Gaussian blur on R and G channels
|
|
41
|
+
vec2 gaussBlur9x9RG(sampler2D tex, vec2 uv, float radius) {
|
|
42
|
+
vec2 texel = 1.0 / vec2(textureSize(tex, 0));
|
|
43
|
+
vec2 r = max(radius, 0.0) * texel;
|
|
44
|
+
// Pascal's row 8: sum = 256, 2D norm = 65536
|
|
45
|
+
const float k[9] = float[9](1.0, 8.0, 28.0, 56.0, 70.0, 56.0, 28.0, 8.0, 1.0);
|
|
46
|
+
vec2 sum = vec2(0.0);
|
|
47
|
+
|
|
48
|
+
for (int j = -4; j <= 4; ++j) {
|
|
49
|
+
float wy = k[j + 4];
|
|
50
|
+
for (int i = -4; i <= 4; ++i) {
|
|
51
|
+
float w = k[i + 4] * wy;
|
|
52
|
+
vec2 off = vec2(float(i) * r.x, float(j) * r.y);
|
|
53
|
+
sum += w * texture(tex, uv + off).rg;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return sum / 65536.0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
float sst(float a, float b, float x) {
|
|
61
|
+
return smoothstep(a, b, x);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
void main() {
|
|
65
|
+
float time = u_time;
|
|
66
|
+
|
|
67
|
+
float roundness = 0.;
|
|
68
|
+
float imgAlpha = 0.;
|
|
69
|
+
|
|
70
|
+
if (u_isImage == true) {
|
|
71
|
+
// Image sampling (UV scaled inward to account for padding)
|
|
72
|
+
vec2 imageUV = v_imageUV;
|
|
73
|
+
imageUV -= .5;
|
|
74
|
+
imageUV *= .95;
|
|
75
|
+
imageUV += .5;
|
|
76
|
+
|
|
77
|
+
// Blurred image: x = roundness, y = alpha
|
|
78
|
+
vec2 blurred = gaussBlur9x9RG(u_image, imageUV, 10.);
|
|
79
|
+
roundness = 1. - blurred.x;
|
|
80
|
+
vec2 texelA = 1.0 / vec2(textureSize(u_image, 0));
|
|
81
|
+
const float k3[3] = float[3](1.0, 2.0, 1.0);
|
|
82
|
+
for (int j = -1; j <= 1; ++j) {
|
|
83
|
+
for (int i = -1; i <= 1; ++i) {
|
|
84
|
+
imgAlpha += k3[i + 1] * k3[j + 1] * texture(u_image, imageUV + vec2(float(i) * texelA.x, float(j) * texelA.y)).g;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
imgAlpha /= 16.0;
|
|
88
|
+
} else {
|
|
89
|
+
vec2 uv = v_objectUV + .5;
|
|
90
|
+
uv.y = 1. - uv.y;
|
|
91
|
+
float edge = 0.;
|
|
92
|
+
|
|
93
|
+
if (u_shape < 1.) {
|
|
94
|
+
// full-fill on canvas
|
|
95
|
+
vec2 borderUV = v_responsiveUV + .5;
|
|
96
|
+
vec2 mask = min(borderUV, 1. - borderUV);
|
|
97
|
+
vec2 pixel_thickness = min(250. / v_responsiveBoxGivenSize, vec2(.5));
|
|
98
|
+
float maskX = smoothstep(0.0, pixel_thickness.x, mask.x);
|
|
99
|
+
float maskY = smoothstep(0.0, pixel_thickness.y, mask.y);
|
|
100
|
+
maskX = pow(maskX, .25);
|
|
101
|
+
maskY = pow(maskY, .25);
|
|
102
|
+
edge = clamp(1. - maskX * maskY, 0., 1.);
|
|
103
|
+
} else if (u_shape < 2.) {
|
|
104
|
+
// circle
|
|
105
|
+
vec2 shapeUV = uv - .5;
|
|
106
|
+
shapeUV *= .67;
|
|
107
|
+
edge = pow(clamp(3. * length(shapeUV), 0., 1.), 18.);
|
|
108
|
+
} else if (u_shape < 3.) {
|
|
109
|
+
// daisy
|
|
110
|
+
vec2 shapeUV = uv - .5;
|
|
111
|
+
shapeUV *= 1.68;
|
|
112
|
+
|
|
113
|
+
float r = length(shapeUV) * 2.;
|
|
114
|
+
float a = atan(shapeUV.y, shapeUV.x) + .2;
|
|
115
|
+
r *= (1. + .05 * sin(3. * a + 2. * time));
|
|
116
|
+
float f = abs(cos(a * 3.));
|
|
117
|
+
edge = smoothstep(f, f + .7, r);
|
|
118
|
+
edge *= edge;
|
|
119
|
+
} else if (u_shape < 4.) {
|
|
120
|
+
// diamond
|
|
121
|
+
vec2 shapeUV = uv - .5;
|
|
122
|
+
shapeUV = rotate(shapeUV, .25 * PI);
|
|
123
|
+
shapeUV *= 1.42;
|
|
124
|
+
shapeUV += .5;
|
|
125
|
+
vec2 mask = min(shapeUV, 1. - shapeUV);
|
|
126
|
+
vec2 pixel_thickness = vec2(.15);
|
|
127
|
+
float maskX = smoothstep(0.0, pixel_thickness.x, mask.x);
|
|
128
|
+
float maskY = smoothstep(0.0, pixel_thickness.y, mask.y);
|
|
129
|
+
maskX = pow(maskX, .25);
|
|
130
|
+
maskY = pow(maskY, .25);
|
|
131
|
+
edge = clamp(1. - maskX * maskY, 0., 1.);
|
|
132
|
+
} else if (u_shape < 5.) {
|
|
133
|
+
// metaballs
|
|
134
|
+
vec2 shapeUV = uv - .5;
|
|
135
|
+
shapeUV *= 1.3;
|
|
136
|
+
edge = 0.;
|
|
137
|
+
for (int i = 0; i < 5; i++) {
|
|
138
|
+
float fi = float(i);
|
|
139
|
+
float speed = 1.5 + 2./3. * sin(fi * 12.345);
|
|
140
|
+
float angle = -fi * 1.5;
|
|
141
|
+
vec2 dir1 = vec2(cos(angle), sin(angle));
|
|
142
|
+
vec2 dir2 = vec2(cos(angle + 1.57), sin(angle + 1.));
|
|
143
|
+
vec2 traj = .4 * (dir1 * sin(time * speed + fi * 1.23) + dir2 * cos(time * (speed * 0.7) + fi * 2.17));
|
|
144
|
+
float d = length(shapeUV + traj);
|
|
145
|
+
edge += pow(1.0 - clamp(d, 0.0, 1.0), 4.0);
|
|
146
|
+
}
|
|
147
|
+
edge = 1. - smoothstep(.65, .9, edge);
|
|
148
|
+
edge = pow(edge, 4.);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
imgAlpha = 1. - smoothstep(.9 - 2. * fwidth(edge), .9, edge);
|
|
152
|
+
roundness = 1. - edge;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Smoke UV setup
|
|
156
|
+
vec2 smokeUV = v_objectUV;
|
|
157
|
+
smokeUV = rotate(smokeUV, u_angle * PI / 180.);
|
|
158
|
+
smokeUV *= mix(4., 1., u_size);
|
|
159
|
+
|
|
160
|
+
// Two swirl paths: inner (shape-masked) and outer (free), each with independent distortion
|
|
161
|
+
vec2 innerUV = smokeUV;
|
|
162
|
+
vec2 outerUV = smokeUV;
|
|
163
|
+
|
|
164
|
+
// Vertical displacement \u2014 applied independently to inner and outer
|
|
165
|
+
innerUV.y += u_innerDistortion * (1. - sst(0., 1., length(.4 * innerUV)));
|
|
166
|
+
innerUV.y -= .4 * u_innerDistortion;
|
|
167
|
+
innerUV.y += .7 * u_offset * roundness;
|
|
168
|
+
|
|
169
|
+
outerUV.y += u_outerDistortion * (1. - sst(0., 1., length(.4 * outerUV)));
|
|
170
|
+
outerUV.y -= .4 * u_outerDistortion;
|
|
171
|
+
|
|
172
|
+
float innerSwirl = u_innerDistortion * roundness;
|
|
173
|
+
float outerSwirl = u_outerDistortion;
|
|
174
|
+
|
|
175
|
+
for (int i = 1; i < 5; i++) {
|
|
176
|
+
float fi = float(i);
|
|
177
|
+
|
|
178
|
+
float stretchIn = max(length(dFdx(innerUV)), length(dFdy(innerUV)));
|
|
179
|
+
float dampenIn = 1. / (1. + stretchIn * 8.);
|
|
180
|
+
float sIn = innerSwirl * dampenIn;
|
|
181
|
+
innerUV.x += sIn / fi * cos(time + fi * 2.9 * innerUV.y);
|
|
182
|
+
innerUV.y += sIn / fi * cos(time + fi * 1.5 * innerUV.x);
|
|
183
|
+
|
|
184
|
+
float stretchOut = max(length(dFdx(outerUV)), length(dFdy(outerUV)));
|
|
185
|
+
float dampenOut = 1. / (1. + stretchOut * 8.);
|
|
186
|
+
float sOut = outerSwirl * dampenOut;
|
|
187
|
+
outerUV.x += sOut / fi * cos(time + fi * 2.9 * outerUV.y);
|
|
188
|
+
outerUV.y += sOut / fi * cos(time + fi * 1.5 * outerUV.x);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Smoke shapes from swirl fields
|
|
192
|
+
float innerShape = exp(-1.5 * dot(innerUV, innerUV));
|
|
193
|
+
float outerShape = exp(-1.5 * dot(outerUV, outerUV));
|
|
194
|
+
|
|
195
|
+
// Visibility masks
|
|
196
|
+
float outerMask = pow(u_outerGlow, 2.) * (1. - imgAlpha);
|
|
197
|
+
float innerMask = (.01 + .99 * u_innerGlow) * imgAlpha;
|
|
198
|
+
|
|
199
|
+
innerShape *= innerMask;
|
|
200
|
+
outerShape *= outerMask;
|
|
201
|
+
|
|
202
|
+
// Color gradient
|
|
203
|
+
float mixer = (innerShape + outerShape) * u_colorsCount;
|
|
204
|
+
vec4 gradient = u_colors[0];
|
|
205
|
+
gradient.rgb *= gradient.a;
|
|
206
|
+
|
|
207
|
+
float smokeMask = 0.;
|
|
208
|
+
for (int i = 1; i < ${gemSmokeMeta.maxColorCount + 1}; i++) {
|
|
209
|
+
if (i > int(u_colorsCount)) break;
|
|
210
|
+
|
|
211
|
+
float m = sst(0., 1., clamp(mixer - float(i - 1), 0., 1.));
|
|
212
|
+
if (i == 1) smokeMask = m;
|
|
213
|
+
|
|
214
|
+
vec4 c = u_colors[i - 1];
|
|
215
|
+
c.rgb *= c.a;
|
|
216
|
+
gradient = mix(gradient, c, m);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Compositing (premultiplied alpha, front-to-back)
|
|
220
|
+
vec3 color = gradient.rgb * smokeMask;
|
|
221
|
+
float opacity = gradient.a * smokeMask;
|
|
222
|
+
|
|
223
|
+
float innerOpacity = u_colorInner.a * imgAlpha;
|
|
224
|
+
vec3 innerColor = u_colorInner.rgb * innerOpacity;
|
|
225
|
+
color += innerColor * (1.0 - opacity);
|
|
226
|
+
opacity += innerOpacity * (1.0 - opacity);
|
|
227
|
+
|
|
228
|
+
vec3 backColor = u_colorBack.rgb * u_colorBack.a;
|
|
229
|
+
color += backColor * (1.0 - opacity);
|
|
230
|
+
opacity += u_colorBack.a * (1.0 - opacity);
|
|
231
|
+
|
|
232
|
+
fragColor = vec4(color, opacity);
|
|
233
|
+
}
|
|
234
|
+
`;
|
|
235
|
+
const POISSON_CONFIG_OPTIMIZED = {
|
|
236
|
+
measurePerformance: false,
|
|
237
|
+
// Set to true to see performance metrics
|
|
238
|
+
workingSize: 512,
|
|
239
|
+
// Size to solve Poisson at (will upscale to original size)
|
|
240
|
+
iterations: 32
|
|
241
|
+
// SOR converges ~2-20x faster than standard Gauss-Seidel
|
|
242
|
+
};
|
|
243
|
+
function toProcessedGemSmoke(file) {
|
|
244
|
+
const canvas = document.createElement("canvas");
|
|
245
|
+
const ctx = canvas.getContext("2d");
|
|
246
|
+
const isBlob = typeof file === "string" && file.startsWith("blob:");
|
|
247
|
+
return new Promise((resolve, reject) => {
|
|
248
|
+
if (!file || !ctx) {
|
|
249
|
+
reject(new Error("Invalid file or canvas context"));
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const blobContentTypePromise = isBlob && fetch(file).then((res) => res.headers.get("Content-Type"));
|
|
253
|
+
const img = new Image();
|
|
254
|
+
img.crossOrigin = "anonymous";
|
|
255
|
+
const totalStartTime = performance.now();
|
|
256
|
+
img.onload = async () => {
|
|
257
|
+
let isSVG;
|
|
258
|
+
const blobContentType = await blobContentTypePromise;
|
|
259
|
+
if (blobContentType) {
|
|
260
|
+
isSVG = blobContentType === "image/svg+xml";
|
|
261
|
+
} else if (typeof file === "string") {
|
|
262
|
+
isSVG = file.endsWith(".svg") || file.startsWith("data:image/svg+xml");
|
|
263
|
+
} else {
|
|
264
|
+
isSVG = file.type === "image/svg+xml";
|
|
265
|
+
}
|
|
266
|
+
let originalWidth = img.width || img.naturalWidth;
|
|
267
|
+
let originalHeight = img.height || img.naturalHeight;
|
|
268
|
+
if (isSVG) {
|
|
269
|
+
const svgMaxSize = 4096;
|
|
270
|
+
const aspectRatio = originalWidth / originalHeight;
|
|
271
|
+
if (originalWidth > originalHeight) {
|
|
272
|
+
originalWidth = svgMaxSize;
|
|
273
|
+
originalHeight = svgMaxSize / aspectRatio;
|
|
274
|
+
} else {
|
|
275
|
+
originalHeight = svgMaxSize;
|
|
276
|
+
originalWidth = svgMaxSize * aspectRatio;
|
|
277
|
+
}
|
|
278
|
+
img.width = originalWidth;
|
|
279
|
+
img.height = originalHeight;
|
|
280
|
+
}
|
|
281
|
+
const minDimension = Math.min(originalWidth, originalHeight);
|
|
282
|
+
const targetSize = POISSON_CONFIG_OPTIMIZED.workingSize;
|
|
283
|
+
const scaleFactor = targetSize / minDimension;
|
|
284
|
+
const width = Math.round(originalWidth * scaleFactor);
|
|
285
|
+
const height = Math.round(originalHeight * scaleFactor);
|
|
286
|
+
if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {
|
|
287
|
+
console.log(`[Processing Mode]`);
|
|
288
|
+
console.log(` Original: ${originalWidth}\xD7${originalHeight}`);
|
|
289
|
+
console.log(` Working: ${width}\xD7${height} (${(scaleFactor * 100).toFixed(1)}% scale)`);
|
|
290
|
+
if (scaleFactor < 1) {
|
|
291
|
+
console.log(` Speedup: ~${Math.round(1 / (scaleFactor * scaleFactor))}\xD7`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
canvas.width = originalWidth;
|
|
295
|
+
canvas.height = originalHeight;
|
|
296
|
+
const paddingSize = 0.025;
|
|
297
|
+
const padX = Math.ceil(width * paddingSize);
|
|
298
|
+
const padY = Math.ceil(height * paddingSize);
|
|
299
|
+
const imgW = width - 2 * padX;
|
|
300
|
+
const imgH = height - 2 * padY;
|
|
301
|
+
const shapeCanvas = document.createElement("canvas");
|
|
302
|
+
shapeCanvas.width = width;
|
|
303
|
+
shapeCanvas.height = height;
|
|
304
|
+
const shapeCtx = shapeCanvas.getContext("2d");
|
|
305
|
+
shapeCtx.drawImage(img, padX, padY, imgW, imgH);
|
|
306
|
+
const startMask = performance.now();
|
|
307
|
+
const shapeImageData = shapeCtx.getImageData(0, 0, width, height);
|
|
308
|
+
const data = shapeImageData.data;
|
|
309
|
+
const shapeMask = new Uint8Array(width * height);
|
|
310
|
+
const boundaryMask = new Uint8Array(width * height);
|
|
311
|
+
let shapePixelCount = 0;
|
|
312
|
+
for (let i = 0, idx = 0; i < data.length; i += 4, idx++) {
|
|
313
|
+
const a = data[i + 3];
|
|
314
|
+
const isShape = a === 0 ? 0 : 1;
|
|
315
|
+
shapeMask[idx] = isShape;
|
|
316
|
+
shapePixelCount += isShape;
|
|
317
|
+
}
|
|
318
|
+
const boundaryIndices = [];
|
|
319
|
+
const interiorIndices = [];
|
|
320
|
+
for (let y = 0; y < height; y++) {
|
|
321
|
+
for (let x = 0; x < width; x++) {
|
|
322
|
+
const idx = y * width + x;
|
|
323
|
+
if (!shapeMask[idx]) continue;
|
|
324
|
+
let isBoundary = false;
|
|
325
|
+
if (x === 0 || x === width - 1 || y === 0 || y === height - 1) {
|
|
326
|
+
isBoundary = true;
|
|
327
|
+
} else {
|
|
328
|
+
isBoundary = !shapeMask[idx - 1] || // left
|
|
329
|
+
!shapeMask[idx + 1] || // right
|
|
330
|
+
!shapeMask[idx - width] || // top
|
|
331
|
+
!shapeMask[idx + width] || // bottom
|
|
332
|
+
!shapeMask[idx - width - 1] || // top-left
|
|
333
|
+
!shapeMask[idx - width + 1] || // top-right
|
|
334
|
+
!shapeMask[idx + width - 1] || // bottom-left
|
|
335
|
+
!shapeMask[idx + width + 1];
|
|
336
|
+
}
|
|
337
|
+
if (isBoundary) {
|
|
338
|
+
boundaryMask[idx] = 1;
|
|
339
|
+
boundaryIndices.push(idx);
|
|
340
|
+
} else {
|
|
341
|
+
interiorIndices.push(idx);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {
|
|
346
|
+
console.log(`[Mask Building] Time: ${(performance.now() - startMask).toFixed(2)}ms`);
|
|
347
|
+
console.log(
|
|
348
|
+
` Shape pixels: ${shapePixelCount} / ${width * height} (${(shapePixelCount / (width * height) * 100).toFixed(1)}%)`
|
|
349
|
+
);
|
|
350
|
+
console.log(` Interior pixels: ${interiorIndices.length}`);
|
|
351
|
+
console.log(` Boundary pixels: ${boundaryIndices.length}`);
|
|
352
|
+
}
|
|
353
|
+
const sparseData = buildSparseData(
|
|
354
|
+
shapeMask,
|
|
355
|
+
boundaryMask,
|
|
356
|
+
new Uint32Array(interiorIndices),
|
|
357
|
+
new Uint32Array(boundaryIndices),
|
|
358
|
+
width,
|
|
359
|
+
height
|
|
360
|
+
);
|
|
361
|
+
const startSolve = performance.now();
|
|
362
|
+
const u = solvePoissonSparse(sparseData, shapeMask, boundaryMask, width, height);
|
|
363
|
+
if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {
|
|
364
|
+
console.log(`[Poisson Solve] Time: ${(performance.now() - startSolve).toFixed(2)}ms`);
|
|
365
|
+
}
|
|
366
|
+
let maxVal = 0;
|
|
367
|
+
let finalImageData;
|
|
368
|
+
for (let i = 0; i < interiorIndices.length; i++) {
|
|
369
|
+
const idx = interiorIndices[i];
|
|
370
|
+
if (u[idx] > maxVal) maxVal = u[idx];
|
|
371
|
+
}
|
|
372
|
+
const tempCanvas = document.createElement("canvas");
|
|
373
|
+
tempCanvas.width = width;
|
|
374
|
+
tempCanvas.height = height;
|
|
375
|
+
const tempCtx = tempCanvas.getContext("2d");
|
|
376
|
+
const tempImg = tempCtx.createImageData(width, height);
|
|
377
|
+
for (let y = 0; y < height; y++) {
|
|
378
|
+
for (let x = 0; x < width; x++) {
|
|
379
|
+
const idx = y * width + x;
|
|
380
|
+
const px = idx * 4;
|
|
381
|
+
if (!shapeMask[idx]) {
|
|
382
|
+
tempImg.data[px] = 255;
|
|
383
|
+
tempImg.data[px + 1] = 255;
|
|
384
|
+
tempImg.data[px + 2] = 255;
|
|
385
|
+
tempImg.data[px + 3] = 0;
|
|
386
|
+
} else {
|
|
387
|
+
const poissonRatio = u[idx] / maxVal;
|
|
388
|
+
let gray = 255 * (1 - poissonRatio);
|
|
389
|
+
tempImg.data[px] = gray;
|
|
390
|
+
tempImg.data[px + 1] = gray;
|
|
391
|
+
tempImg.data[px + 2] = gray;
|
|
392
|
+
tempImg.data[px + 3] = 255;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
tempCtx.putImageData(tempImg, 0, 0);
|
|
397
|
+
ctx.imageSmoothingEnabled = true;
|
|
398
|
+
ctx.imageSmoothingQuality = "high";
|
|
399
|
+
ctx.drawImage(tempCanvas, 0, 0, width, height, 0, 0, originalWidth, originalHeight);
|
|
400
|
+
const outImg = ctx.getImageData(0, 0, originalWidth, originalHeight);
|
|
401
|
+
const origPadX = Math.ceil(originalWidth * paddingSize);
|
|
402
|
+
const origPadY = Math.ceil(originalHeight * paddingSize);
|
|
403
|
+
const originalCanvas = document.createElement("canvas");
|
|
404
|
+
originalCanvas.width = originalWidth;
|
|
405
|
+
originalCanvas.height = originalHeight;
|
|
406
|
+
const originalCtx = originalCanvas.getContext("2d");
|
|
407
|
+
originalCtx.drawImage(img, origPadX, origPadY, originalWidth - 2 * origPadX, originalHeight - 2 * origPadY);
|
|
408
|
+
const originalData = originalCtx.getImageData(0, 0, originalWidth, originalHeight);
|
|
409
|
+
for (let i = 0; i < outImg.data.length; i += 4) {
|
|
410
|
+
const a = originalData.data[i + 3];
|
|
411
|
+
const upscaledAlpha = outImg.data[i + 3];
|
|
412
|
+
if (a === 0) {
|
|
413
|
+
outImg.data[i] = 255;
|
|
414
|
+
outImg.data[i + 1] = 0;
|
|
415
|
+
} else {
|
|
416
|
+
outImg.data[i] = upscaledAlpha === 0 ? 0 : outImg.data[i];
|
|
417
|
+
outImg.data[i + 1] = a;
|
|
418
|
+
}
|
|
419
|
+
outImg.data[i + 2] = 255;
|
|
420
|
+
outImg.data[i + 3] = 255;
|
|
421
|
+
}
|
|
422
|
+
ctx.putImageData(outImg, 0, 0);
|
|
423
|
+
finalImageData = outImg;
|
|
424
|
+
canvas.toBlob((blob) => {
|
|
425
|
+
if (!blob) {
|
|
426
|
+
reject(new Error("Failed to create PNG blob"));
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {
|
|
430
|
+
const totalTime = performance.now() - totalStartTime;
|
|
431
|
+
console.log(`[Total Processing Time] ${totalTime.toFixed(2)}ms`);
|
|
432
|
+
if (scaleFactor < 1) {
|
|
433
|
+
const estimatedFullResTime = totalTime * Math.pow(originalWidth * originalHeight / (width * height), 1.5);
|
|
434
|
+
console.log(`[Estimated time at full resolution] ~${estimatedFullResTime.toFixed(0)}ms`);
|
|
435
|
+
console.log(
|
|
436
|
+
`[Time saved] ~${(estimatedFullResTime - totalTime).toFixed(0)}ms (${Math.round(estimatedFullResTime / totalTime)}\xD7 faster)`
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
resolve({
|
|
441
|
+
imageData: finalImageData,
|
|
442
|
+
pngBlob: blob
|
|
443
|
+
});
|
|
444
|
+
}, "image/png");
|
|
445
|
+
};
|
|
446
|
+
img.onerror = () => reject(new Error("Failed to load image"));
|
|
447
|
+
img.src = typeof file === "string" ? file : URL.createObjectURL(file);
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
function buildSparseData(shapeMask, boundaryMask, interiorPixels, boundaryPixels, width, height) {
|
|
451
|
+
const pixelCount = interiorPixels.length;
|
|
452
|
+
const neighborIndices = new Int32Array(pixelCount * 4);
|
|
453
|
+
for (let i = 0; i < pixelCount; i++) {
|
|
454
|
+
const idx = interiorPixels[i];
|
|
455
|
+
const x = idx % width;
|
|
456
|
+
const y = Math.floor(idx / width);
|
|
457
|
+
neighborIndices[i * 4 + 0] = x < width - 1 && shapeMask[idx + 1] ? idx + 1 : -1;
|
|
458
|
+
neighborIndices[i * 4 + 1] = x > 0 && shapeMask[idx - 1] ? idx - 1 : -1;
|
|
459
|
+
neighborIndices[i * 4 + 2] = y > 0 && shapeMask[idx - width] ? idx - width : -1;
|
|
460
|
+
neighborIndices[i * 4 + 3] = y < height - 1 && shapeMask[idx + width] ? idx + width : -1;
|
|
461
|
+
}
|
|
462
|
+
return {
|
|
463
|
+
interiorPixels,
|
|
464
|
+
boundaryPixels,
|
|
465
|
+
pixelCount,
|
|
466
|
+
neighborIndices
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
function solvePoissonSparse(sparseData, shapeMask, boundaryMask, width, height) {
|
|
470
|
+
const ITERATIONS = POISSON_CONFIG_OPTIMIZED.iterations;
|
|
471
|
+
const C = 0.01;
|
|
472
|
+
const u = new Float32Array(width * height);
|
|
473
|
+
const { interiorPixels, neighborIndices, pixelCount } = sparseData;
|
|
474
|
+
const startTime = performance.now();
|
|
475
|
+
const omega = 1.9;
|
|
476
|
+
const redPixels = [];
|
|
477
|
+
const blackPixels = [];
|
|
478
|
+
for (let i = 0; i < pixelCount; i++) {
|
|
479
|
+
const idx = interiorPixels[i];
|
|
480
|
+
const x = idx % width;
|
|
481
|
+
const y = Math.floor(idx / width);
|
|
482
|
+
if ((x + y) % 2 === 0) {
|
|
483
|
+
redPixels.push(i);
|
|
484
|
+
} else {
|
|
485
|
+
blackPixels.push(i);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
for (let iter = 0; iter < ITERATIONS; iter++) {
|
|
489
|
+
for (const i of redPixels) {
|
|
490
|
+
const idx = interiorPixels[i];
|
|
491
|
+
const eastIdx = neighborIndices[i * 4 + 0];
|
|
492
|
+
const westIdx = neighborIndices[i * 4 + 1];
|
|
493
|
+
const northIdx = neighborIndices[i * 4 + 2];
|
|
494
|
+
const southIdx = neighborIndices[i * 4 + 3];
|
|
495
|
+
let sumN = 0;
|
|
496
|
+
if (eastIdx >= 0) sumN += u[eastIdx];
|
|
497
|
+
if (westIdx >= 0) sumN += u[westIdx];
|
|
498
|
+
if (northIdx >= 0) sumN += u[northIdx];
|
|
499
|
+
if (southIdx >= 0) sumN += u[southIdx];
|
|
500
|
+
const newValue = (C + sumN) / 4;
|
|
501
|
+
u[idx] = omega * newValue + (1 - omega) * u[idx];
|
|
502
|
+
}
|
|
503
|
+
for (const i of blackPixels) {
|
|
504
|
+
const idx = interiorPixels[i];
|
|
505
|
+
const eastIdx = neighborIndices[i * 4 + 0];
|
|
506
|
+
const westIdx = neighborIndices[i * 4 + 1];
|
|
507
|
+
const northIdx = neighborIndices[i * 4 + 2];
|
|
508
|
+
const southIdx = neighborIndices[i * 4 + 3];
|
|
509
|
+
let sumN = 0;
|
|
510
|
+
if (eastIdx >= 0) sumN += u[eastIdx];
|
|
511
|
+
if (westIdx >= 0) sumN += u[westIdx];
|
|
512
|
+
if (northIdx >= 0) sumN += u[northIdx];
|
|
513
|
+
if (southIdx >= 0) sumN += u[southIdx];
|
|
514
|
+
const newValue = (C + sumN) / 4;
|
|
515
|
+
u[idx] = omega * newValue + (1 - omega) * u[idx];
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
const tmp = new Float32Array(width * height);
|
|
519
|
+
for (let smooth = 0; smooth < 3; smooth++) {
|
|
520
|
+
tmp.set(u);
|
|
521
|
+
for (let i = 0; i < pixelCount; i++) {
|
|
522
|
+
const idx = interiorPixels[i];
|
|
523
|
+
const eastIdx = neighborIndices[i * 4 + 0];
|
|
524
|
+
const westIdx = neighborIndices[i * 4 + 1];
|
|
525
|
+
const northIdx = neighborIndices[i * 4 + 2];
|
|
526
|
+
const southIdx = neighborIndices[i * 4 + 3];
|
|
527
|
+
let sum = 0;
|
|
528
|
+
let count = 0;
|
|
529
|
+
if (eastIdx >= 0) {
|
|
530
|
+
sum += tmp[eastIdx];
|
|
531
|
+
count++;
|
|
532
|
+
}
|
|
533
|
+
if (westIdx >= 0) {
|
|
534
|
+
sum += tmp[westIdx];
|
|
535
|
+
count++;
|
|
536
|
+
}
|
|
537
|
+
if (northIdx >= 0) {
|
|
538
|
+
sum += tmp[northIdx];
|
|
539
|
+
count++;
|
|
540
|
+
}
|
|
541
|
+
if (southIdx >= 0) {
|
|
542
|
+
sum += tmp[southIdx];
|
|
543
|
+
count++;
|
|
544
|
+
}
|
|
545
|
+
u[idx] = count > 0 ? (tmp[idx] + sum / count) * 0.5 : tmp[idx];
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {
|
|
549
|
+
const elapsed = performance.now() - startTime;
|
|
550
|
+
console.log(`[Optimized Poisson Solver (SOR \u03C9=${omega})]`);
|
|
551
|
+
console.log(` Working size: ${width}\xD7${height}`);
|
|
552
|
+
console.log(` Iterations: ${ITERATIONS}`);
|
|
553
|
+
console.log(` Time: ${elapsed.toFixed(2)}ms`);
|
|
554
|
+
console.log(` Interior pixels processed: ${pixelCount}`);
|
|
555
|
+
console.log(` Speed: ${(ITERATIONS * pixelCount / (elapsed * 1e3)).toFixed(2)} Mpixels/sec`);
|
|
556
|
+
}
|
|
557
|
+
return u;
|
|
558
|
+
}
|
|
559
|
+
const GemSmokeShapes = {
|
|
560
|
+
none: 0,
|
|
561
|
+
circle: 1,
|
|
562
|
+
daisy: 2,
|
|
563
|
+
diamond: 3,
|
|
564
|
+
metaballs: 4
|
|
565
|
+
};
|
|
566
|
+
export {
|
|
567
|
+
GemSmokeShapes,
|
|
568
|
+
POISSON_CONFIG_OPTIMIZED,
|
|
569
|
+
gemSmokeFragmentShader,
|
|
570
|
+
gemSmokeMeta,
|
|
571
|
+
toProcessedGemSmoke
|
|
572
|
+
};
|
|
573
|
+
//# sourceMappingURL=gem-smoke.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/shaders/gem-smoke.ts"],
|
|
4
|
+
"sourcesContent": ["import type { vec4 } from '../types.js';\r\nimport type { ShaderMotionParams } from '../shader-mount.js';\r\nimport type { ShaderSizingParams, ShaderSizingUniforms } from '../shader-sizing.js';\r\nimport { rotation2, declarePI } from '../shader-utils.js';\r\n\r\nexport const gemSmokeMeta = {\r\n maxColorCount: 6,\r\n} as const;\r\n\r\n/**\r\n * Animated color fields placed over uploaded logo shape; gives the illusion of smoky noise\r\n * behind the glassy shape.\r\n *\r\n * Fragment shader uniforms:\r\n * - u_time (float): Animation time\r\n * - u_image (sampler2D): Pre-processed source image texture (R = edge gradient, G = alpha)\r\n * - u_colors (vec4[]): Up to 6 smoke colors in RGBA\r\n * - u_colorsCount (float): Number of active colors\r\n * - u_colorBack (vec4): Background color in RGBA\r\n * - u_innerDistortion (float): Power of smoke distortion inside the input shape, needs innerGlow > 0 (0 to 1)\r\n * - u_outerDistortion (float): Power of smoke distortion outside the input shape, needs outerGlow > 0 (0 to 1)\r\n * - u_outerGlow (float): Visibility of smoke shape outside the input shape (0 to 1)\r\n * - u_innerGlow (float): Visibility of smoke shape inside the input shape (0 to 1)\r\n * - u_colorInner (vec4): Additional color inside the input shape, mixing with smoke (RGBA)\r\n * - u_offset (float): Vertical offset of smoke inside the shape, needs innerGlow > 0 (-1 to 1)\r\n * - u_angle (float): Smoke direction in degrees (0 to 360)\r\n * - u_size (float): Size of smoke shape relative to the image box (0 to 1)\r\n * - u_shape (float): Predefined shape mask, needs no image (0 = none, 1 = circle, 2 = daisy, 3 = diamond, 4 = metaballs)\r\n * - u_isImage (bool): Whether an image mask is provided; when true, u_shape is ignored\r\n *\r\n * Vertex shader outputs (used in fragment shader):\r\n * - v_imageUV (vec2): UV coordinates for sampling the source image, with fit, scale, rotation, and offset applied\r\n * - v_objectUV (vec2): Normalized UV coordinates with scale, rotation, and offset applied\r\n * - v_responsiveUV (vec2): Responsive UV coordinates that adapt to canvas aspect ratio (used by the 'none' shape)\r\n * - v_responsiveBoxGivenSize (vec2): Given size of the responsive bounding box (used by the 'none' shape)\r\n *\r\n * Vertex shader uniforms:\r\n * - u_resolution (vec2): Canvas resolution in pixels\r\n * - u_pixelRatio (float): Device pixel ratio\r\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\r\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\r\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\r\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\r\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\r\n * - u_scale (float): Overall zoom level of the graphics (0.1 to 4)\r\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\r\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\r\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\r\n * - u_imageAspectRatio (float): Aspect ratio of the source image\r\n *\r\n */\r\n\r\n// language=GLSL\r\nexport const gemSmokeFragmentShader: string = `#version 300 es\r\nprecision mediump float;\r\n\r\nin mediump vec2 v_imageUV;\r\nin mediump vec2 v_objectUV;\r\nin mediump vec2 v_responsiveUV;\r\nin mediump vec2 v_responsiveBoxGivenSize;\r\n \r\nout vec4 fragColor;\r\n\r\nuniform sampler2D u_image;\r\nuniform float u_shape;\r\nuniform bool u_isImage;\r\n\r\nuniform float u_time;\r\nuniform vec4 u_colors[${gemSmokeMeta.maxColorCount}];\r\nuniform float u_colorsCount;\r\nuniform vec4 u_colorBack;\r\nuniform vec4 u_colorInner;\r\nuniform float u_innerDistortion;\r\nuniform float u_outerDistortion;\r\nuniform float u_outerGlow;\r\nuniform float u_innerGlow;\r\nuniform float u_offset;\r\nuniform float u_angle;\r\nuniform float u_size;\r\n\r\n${declarePI}\r\n${rotation2}\r\n\r\n// 9x9 Gaussian blur on R and G channels\r\nvec2 gaussBlur9x9RG(sampler2D tex, vec2 uv, float radius) {\r\n vec2 texel = 1.0 / vec2(textureSize(tex, 0));\r\n vec2 r = max(radius, 0.0) * texel;\r\n // Pascal's row 8: sum = 256, 2D norm = 65536\r\n const float k[9] = float[9](1.0, 8.0, 28.0, 56.0, 70.0, 56.0, 28.0, 8.0, 1.0);\r\n vec2 sum = vec2(0.0);\r\n\r\n for (int j = -4; j <= 4; ++j) {\r\n float wy = k[j + 4];\r\n for (int i = -4; i <= 4; ++i) {\r\n float w = k[i + 4] * wy;\r\n vec2 off = vec2(float(i) * r.x, float(j) * r.y);\r\n sum += w * texture(tex, uv + off).rg;\r\n }\r\n }\r\n\r\n return sum / 65536.0;\r\n}\r\n\r\nfloat sst(float a, float b, float x) {\r\n return smoothstep(a, b, x);\r\n}\r\n\r\nvoid main() {\r\n float time = u_time;\r\n\r\n float roundness = 0.;\r\n float imgAlpha = 0.;\r\n\r\n if (u_isImage == true) {\r\n // Image sampling (UV scaled inward to account for padding)\r\n vec2 imageUV = v_imageUV;\r\n imageUV -= .5;\r\n imageUV *= .95;\r\n imageUV += .5;\r\n\r\n // Blurred image: x = roundness, y = alpha\r\n vec2 blurred = gaussBlur9x9RG(u_image, imageUV, 10.);\r\n roundness = 1. - blurred.x;\r\n vec2 texelA = 1.0 / vec2(textureSize(u_image, 0));\r\n const float k3[3] = float[3](1.0, 2.0, 1.0);\r\n for (int j = -1; j <= 1; ++j) {\r\n for (int i = -1; i <= 1; ++i) {\r\n imgAlpha += k3[i + 1] * k3[j + 1] * texture(u_image, imageUV + vec2(float(i) * texelA.x, float(j) * texelA.y)).g;\r\n }\r\n }\r\n imgAlpha /= 16.0;\r\n } else {\r\n vec2 uv = v_objectUV + .5;\r\n uv.y = 1. - uv.y;\r\n float edge = 0.;\r\n\r\n if (u_shape < 1.) {\r\n // full-fill on canvas\r\n vec2 borderUV = v_responsiveUV + .5;\r\n vec2 mask = min(borderUV, 1. - borderUV);\r\n vec2 pixel_thickness = min(250. / v_responsiveBoxGivenSize, vec2(.5));\r\n float maskX = smoothstep(0.0, pixel_thickness.x, mask.x);\r\n float maskY = smoothstep(0.0, pixel_thickness.y, mask.y);\r\n maskX = pow(maskX, .25);\r\n maskY = pow(maskY, .25);\r\n edge = clamp(1. - maskX * maskY, 0., 1.);\r\n } else if (u_shape < 2.) {\r\n // circle\r\n vec2 shapeUV = uv - .5;\r\n shapeUV *= .67;\r\n edge = pow(clamp(3. * length(shapeUV), 0., 1.), 18.);\r\n } else if (u_shape < 3.) {\r\n // daisy\r\n vec2 shapeUV = uv - .5;\r\n shapeUV *= 1.68;\r\n\r\n float r = length(shapeUV) * 2.;\r\n float a = atan(shapeUV.y, shapeUV.x) + .2;\r\n r *= (1. + .05 * sin(3. * a + 2. * time));\r\n float f = abs(cos(a * 3.));\r\n edge = smoothstep(f, f + .7, r);\r\n edge *= edge;\r\n } else if (u_shape < 4.) {\r\n // diamond\r\n vec2 shapeUV = uv - .5;\r\n shapeUV = rotate(shapeUV, .25 * PI);\r\n shapeUV *= 1.42;\r\n shapeUV += .5;\r\n vec2 mask = min(shapeUV, 1. - shapeUV);\r\n vec2 pixel_thickness = vec2(.15);\r\n float maskX = smoothstep(0.0, pixel_thickness.x, mask.x);\r\n float maskY = smoothstep(0.0, pixel_thickness.y, mask.y);\r\n maskX = pow(maskX, .25);\r\n maskY = pow(maskY, .25);\r\n edge = clamp(1. - maskX * maskY, 0., 1.);\r\n } else if (u_shape < 5.) {\r\n // metaballs\r\n vec2 shapeUV = uv - .5;\r\n shapeUV *= 1.3;\r\n edge = 0.;\r\n for (int i = 0; i < 5; i++) {\r\n float fi = float(i);\r\n float speed = 1.5 + 2./3. * sin(fi * 12.345);\r\n float angle = -fi * 1.5;\r\n vec2 dir1 = vec2(cos(angle), sin(angle));\r\n vec2 dir2 = vec2(cos(angle + 1.57), sin(angle + 1.));\r\n vec2 traj = .4 * (dir1 * sin(time * speed + fi * 1.23) + dir2 * cos(time * (speed * 0.7) + fi * 2.17));\r\n float d = length(shapeUV + traj);\r\n edge += pow(1.0 - clamp(d, 0.0, 1.0), 4.0);\r\n }\r\n edge = 1. - smoothstep(.65, .9, edge);\r\n edge = pow(edge, 4.);\r\n }\r\n\r\n imgAlpha = 1. - smoothstep(.9 - 2. * fwidth(edge), .9, edge);\r\n roundness = 1. - edge;\r\n }\r\n\r\n// Smoke UV setup\r\n vec2 smokeUV = v_objectUV;\r\n smokeUV = rotate(smokeUV, u_angle * PI / 180.);\r\n smokeUV *= mix(4., 1., u_size);\r\n\r\n // Two swirl paths: inner (shape-masked) and outer (free), each with independent distortion\r\n vec2 innerUV = smokeUV;\r\n vec2 outerUV = smokeUV;\r\n\r\n // Vertical displacement \u2014 applied independently to inner and outer\r\n innerUV.y += u_innerDistortion * (1. - sst(0., 1., length(.4 * innerUV)));\r\n innerUV.y -= .4 * u_innerDistortion;\r\n innerUV.y += .7 * u_offset * roundness;\r\n\r\n outerUV.y += u_outerDistortion * (1. - sst(0., 1., length(.4 * outerUV)));\r\n outerUV.y -= .4 * u_outerDistortion;\r\n\r\n float innerSwirl = u_innerDistortion * roundness;\r\n float outerSwirl = u_outerDistortion;\r\n\r\n for (int i = 1; i < 5; i++) {\r\n float fi = float(i);\r\n\r\n float stretchIn = max(length(dFdx(innerUV)), length(dFdy(innerUV)));\r\n float dampenIn = 1. / (1. + stretchIn * 8.);\r\n float sIn = innerSwirl * dampenIn;\r\n innerUV.x += sIn / fi * cos(time + fi * 2.9 * innerUV.y);\r\n innerUV.y += sIn / fi * cos(time + fi * 1.5 * innerUV.x);\r\n\r\n float stretchOut = max(length(dFdx(outerUV)), length(dFdy(outerUV)));\r\n float dampenOut = 1. / (1. + stretchOut * 8.);\r\n float sOut = outerSwirl * dampenOut;\r\n outerUV.x += sOut / fi * cos(time + fi * 2.9 * outerUV.y);\r\n outerUV.y += sOut / fi * cos(time + fi * 1.5 * outerUV.x);\r\n }\r\n\r\n // Smoke shapes from swirl fields\r\n float innerShape = exp(-1.5 * dot(innerUV, innerUV));\r\n float outerShape = exp(-1.5 * dot(outerUV, outerUV));\r\n\r\n // Visibility masks\r\n float outerMask = pow(u_outerGlow, 2.) * (1. - imgAlpha);\r\n float innerMask = (.01 + .99 * u_innerGlow) * imgAlpha;\r\n\r\n innerShape *= innerMask;\r\n outerShape *= outerMask;\r\n\r\n // Color gradient\r\n float mixer = (innerShape + outerShape) * u_colorsCount;\r\n vec4 gradient = u_colors[0];\r\n gradient.rgb *= gradient.a;\r\n\r\n float smokeMask = 0.;\r\n for (int i = 1; i < ${gemSmokeMeta.maxColorCount + 1}; i++) {\r\n if (i > int(u_colorsCount)) break;\r\n\r\n float m = sst(0., 1., clamp(mixer - float(i - 1), 0., 1.));\r\n if (i == 1) smokeMask = m;\r\n\r\n vec4 c = u_colors[i - 1];\r\n c.rgb *= c.a;\r\n gradient = mix(gradient, c, m);\r\n }\r\n\r\n // Compositing (premultiplied alpha, front-to-back)\r\n vec3 color = gradient.rgb * smokeMask;\r\n float opacity = gradient.a * smokeMask;\r\n\r\n float innerOpacity = u_colorInner.a * imgAlpha;\r\n vec3 innerColor = u_colorInner.rgb * innerOpacity;\r\n color += innerColor * (1.0 - opacity);\r\n opacity += innerOpacity * (1.0 - opacity);\r\n\r\n vec3 backColor = u_colorBack.rgb * u_colorBack.a;\r\n color += backColor * (1.0 - opacity);\r\n opacity += u_colorBack.a * (1.0 - opacity);\r\n\r\n fragColor = vec4(color, opacity);\r\n}\r\n`;\r\n\r\n// Configuration for Poisson solver\r\nexport const POISSON_CONFIG_OPTIMIZED = {\r\n measurePerformance: false, // Set to true to see performance metrics\r\n workingSize: 512, // Size to solve Poisson at (will upscale to original size)\r\n iterations: 32, // SOR converges ~2-20x faster than standard Gauss-Seidel\r\n};\r\n\r\n// Precomputed pixel data for sparse processing\r\ninterface SparsePixelData {\r\n interiorPixels: Uint32Array; // Indices of interior pixels\r\n boundaryPixels: Uint32Array; // Indices of boundary pixels\r\n pixelCount: number;\r\n // Neighbor indices for each interior pixel (4 neighbors per pixel)\r\n // Layout: [east, west, north, south] for each pixel\r\n neighborIndices: Int32Array;\r\n}\r\n\r\nexport function toProcessedGemSmoke(file: File | string): Promise<{ imageData: ImageData; pngBlob: Blob }> {\r\n const canvas = document.createElement('canvas');\r\n const ctx = canvas.getContext('2d');\r\n const isBlob = typeof file === 'string' && file.startsWith('blob:');\r\n\r\n return new Promise((resolve, reject) => {\r\n if (!file || !ctx) {\r\n reject(new Error('Invalid file or canvas context'));\r\n return;\r\n }\r\n\r\n const blobContentTypePromise = isBlob && fetch(file).then((res) => res.headers.get('Content-Type'));\r\n const img = new Image();\r\n img.crossOrigin = 'anonymous';\r\n const totalStartTime = performance.now();\r\n\r\n img.onload = async () => {\r\n // Force SVG to load at a high fidelity size if it's an SVG\r\n let isSVG;\r\n\r\n const blobContentType = await blobContentTypePromise;\r\n\r\n if (blobContentType) {\r\n isSVG = blobContentType === 'image/svg+xml';\r\n } else if (typeof file === 'string') {\r\n isSVG = file.endsWith('.svg') || file.startsWith('data:image/svg+xml');\r\n } else {\r\n isSVG = file.type === 'image/svg+xml';\r\n }\r\n\r\n let originalWidth = img.width || img.naturalWidth;\r\n let originalHeight = img.height || img.naturalHeight;\r\n\r\n if (isSVG) {\r\n // Scale SVG to max dimension while preserving aspect ratio\r\n const svgMaxSize = 4096;\r\n const aspectRatio = originalWidth / originalHeight;\r\n\r\n if (originalWidth > originalHeight) {\r\n originalWidth = svgMaxSize;\r\n originalHeight = svgMaxSize / aspectRatio;\r\n } else {\r\n originalHeight = svgMaxSize;\r\n originalWidth = svgMaxSize * aspectRatio;\r\n }\r\n\r\n img.width = originalWidth;\r\n img.height = originalHeight;\r\n }\r\n\r\n // Always scale to working resolution for consistency\r\n const minDimension = Math.min(originalWidth, originalHeight);\r\n const targetSize = POISSON_CONFIG_OPTIMIZED.workingSize;\r\n\r\n // Calculate scale to fit within workingSize\r\n const scaleFactor = targetSize / minDimension;\r\n const width = Math.round(originalWidth * scaleFactor);\r\n const height = Math.round(originalHeight * scaleFactor);\r\n\r\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\r\n console.log(`[Processing Mode]`);\r\n console.log(` Original: ${originalWidth}\u00D7${originalHeight}`);\r\n console.log(` Working: ${width}\u00D7${height} (${(scaleFactor * 100).toFixed(1)}% scale)`);\r\n if (scaleFactor < 1) {\r\n console.log(` Speedup: ~${Math.round(1 / (scaleFactor * scaleFactor))}\u00D7`);\r\n }\r\n }\r\n\r\n canvas.width = originalWidth;\r\n canvas.height = originalHeight;\r\n\r\n const paddingSize = 0.025;\r\n const padX = Math.ceil(width * paddingSize);\r\n const padY = Math.ceil(height * paddingSize);\r\n const imgW = width - 2 * padX;\r\n const imgH = height - 2 * padY;\r\n\r\n // \u2500\u2500 1. Shape canvas (working res): image centered with padding \u2500\u2500\r\n const shapeCanvas = document.createElement('canvas');\r\n shapeCanvas.width = width;\r\n shapeCanvas.height = height;\r\n\r\n const shapeCtx = shapeCanvas.getContext('2d')!;\r\n shapeCtx.drawImage(img, padX, padY, imgW, imgH);\r\n\r\n // 1) Build optimized masks using TypedArrays\r\n const startMask = performance.now();\r\n\r\n const shapeImageData = shapeCtx.getImageData(0, 0, width, height);\r\n const data = shapeImageData.data;\r\n\r\n // Use Uint8Array for masks (1 byte per pixel vs 8+ bytes for boolean array)\r\n const shapeMask = new Uint8Array(width * height);\r\n const boundaryMask = new Uint8Array(width * height);\r\n\r\n // First pass: identify shape pixels\r\n let shapePixelCount = 0;\r\n for (let i = 0, idx = 0; i < data.length; i += 4, idx++) {\r\n const a = data[i + 3];\r\n const isShape = a === 0 ? 0 : 1;\r\n shapeMask[idx] = isShape;\r\n shapePixelCount += isShape;\r\n }\r\n\r\n // 2) Optimized boundary detection using sparse approach\r\n // Only check shape pixels, not all pixels\r\n const boundaryIndices: number[] = [];\r\n const interiorIndices: number[] = [];\r\n\r\n for (let y = 0; y < height; y++) {\r\n for (let x = 0; x < width; x++) {\r\n const idx = y * width + x;\r\n if (!shapeMask[idx]) continue;\r\n\r\n // Check if pixel is on boundary (optimized: early exit)\r\n let isBoundary = false;\r\n\r\n // Check 4-connected neighbors first (most common case)\r\n if (x === 0 || x === width - 1 || y === 0 || y === height - 1) {\r\n isBoundary = true;\r\n } else {\r\n // Check all 8 neighbors (including diagonals) for comprehensive boundary detection\r\n isBoundary =\r\n !shapeMask[idx - 1] || // left\r\n !shapeMask[idx + 1] || // right\r\n !shapeMask[idx - width] || // top\r\n !shapeMask[idx + width] || // bottom\r\n !shapeMask[idx - width - 1] || // top-left\r\n !shapeMask[idx - width + 1] || // top-right\r\n !shapeMask[idx + width - 1] || // bottom-left\r\n !shapeMask[idx + width + 1]; // bottom-right\r\n }\r\n\r\n if (isBoundary) {\r\n boundaryMask[idx] = 1;\r\n boundaryIndices.push(idx);\r\n } else {\r\n interiorIndices.push(idx);\r\n }\r\n }\r\n }\r\n\r\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\r\n console.log(`[Mask Building] Time: ${(performance.now() - startMask).toFixed(2)}ms`);\r\n console.log(\r\n ` Shape pixels: ${shapePixelCount} / ${width * height} (${((shapePixelCount / (width * height)) * 100).toFixed(1)}%)`\r\n );\r\n console.log(` Interior pixels: ${interiorIndices.length}`);\r\n console.log(` Boundary pixels: ${boundaryIndices.length}`);\r\n }\r\n\r\n // 3) Precompute sparse data structure for solver\r\n const sparseData = buildSparseData(\r\n shapeMask,\r\n boundaryMask,\r\n new Uint32Array(interiorIndices),\r\n new Uint32Array(boundaryIndices),\r\n width,\r\n height\r\n );\r\n\r\n // 4) Solve Poisson equation with optimized sparse solver\r\n const startSolve = performance.now();\r\n const u = solvePoissonSparse(sparseData, shapeMask, boundaryMask, width, height);\r\n\r\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\r\n console.log(`[Poisson Solve] Time: ${(performance.now() - startSolve).toFixed(2)}ms`);\r\n }\r\n\r\n // 5) Generate output image\r\n let maxVal = 0;\r\n let finalImageData: ImageData;\r\n\r\n // Only check shape pixels for max value\r\n for (let i = 0; i < interiorIndices.length; i++) {\r\n const idx = interiorIndices[i]!;\r\n if (u[idx]! > maxVal) maxVal = u[idx]!;\r\n }\r\n\r\n // Create roundness image at working resolution\r\n const tempCanvas = document.createElement('canvas');\r\n tempCanvas.width = width;\r\n tempCanvas.height = height;\r\n const tempCtx = tempCanvas.getContext('2d')!;\r\n\r\n const tempImg = tempCtx.createImageData(width, height);\r\n for (let y = 0; y < height; y++) {\r\n for (let x = 0; x < width; x++) {\r\n const idx = y * width + x;\r\n const px = idx * 4;\r\n\r\n if (!shapeMask[idx]) {\r\n tempImg.data[px] = 255;\r\n tempImg.data[px + 1] = 255;\r\n tempImg.data[px + 2] = 255;\r\n tempImg.data[px + 3] = 0; // Alpha = 0 for background\r\n } else {\r\n const poissonRatio = u[idx]! / maxVal;\r\n let gray = 255 * (1 - poissonRatio);\r\n tempImg.data[px] = gray;\r\n tempImg.data[px + 1] = gray;\r\n tempImg.data[px + 2] = gray;\r\n tempImg.data[px + 3] = 255; // Alpha = 255 for shape\r\n }\r\n }\r\n }\r\n tempCtx.putImageData(tempImg, 0, 0);\r\n\r\n // Upscale to original resolution with smooth interpolation\r\n ctx.imageSmoothingEnabled = true;\r\n ctx.imageSmoothingQuality = 'high';\r\n ctx.drawImage(tempCanvas, 0, 0, width, height, 0, 0, originalWidth, originalHeight);\r\n\r\n // Now get the upscaled image data for final output\r\n const outImg = ctx.getImageData(0, 0, originalWidth, originalHeight);\r\n\r\n // Re-apply edges from original resolution with anti-aliasing\r\n // This ensures edges are pixel-perfect while roundness is smooth\r\n const origPadX = Math.ceil(originalWidth * paddingSize);\r\n const origPadY = Math.ceil(originalHeight * paddingSize);\r\n const originalCanvas = document.createElement('canvas');\r\n originalCanvas.width = originalWidth;\r\n originalCanvas.height = originalHeight;\r\n const originalCtx = originalCanvas.getContext('2d')!;\r\n originalCtx.drawImage(img, origPadX, origPadY, originalWidth - 2 * origPadX, originalHeight - 2 * origPadY);\r\n const originalData = originalCtx.getImageData(0, 0, originalWidth, originalHeight);\r\n\r\n // Process each pixel: Red channel = roundness, Alpha channel = original alpha\r\n for (let i = 0; i < outImg.data.length; i += 4) {\r\n const a = originalData.data[i + 3]!;\r\n // Use only alpha to determine background vs shape\r\n const upscaledAlpha = outImg.data[i + 3]!;\r\n if (a === 0) {\r\n // Background pixel\r\n outImg.data[i] = 255;\r\n outImg.data[i + 1] = 0;\r\n } else {\r\n // Red channel carries the roundness\r\n // Check if upscale missed this pixel by looking at alpha channel\r\n // If upscaled alpha is 0, the low-res version thought this was background\r\n outImg.data[i] = upscaledAlpha === 0 ? 0 : outImg.data[i]!; // roundness or 0\r\n outImg.data[i + 1] = a; // original alpha\r\n }\r\n\r\n // Unused channels fixed\r\n outImg.data[i + 2] = 255;\r\n outImg.data[i + 3] = 255;\r\n }\r\n\r\n ctx.putImageData(outImg, 0, 0);\r\n finalImageData = outImg;\r\n canvas.toBlob((blob) => {\r\n if (!blob) {\r\n reject(new Error('Failed to create PNG blob'));\r\n return;\r\n }\r\n\r\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\r\n const totalTime = performance.now() - totalStartTime;\r\n console.log(`[Total Processing Time] ${totalTime.toFixed(2)}ms`);\r\n if (scaleFactor < 1) {\r\n const estimatedFullResTime = totalTime * Math.pow((originalWidth * originalHeight) / (width * height), 1.5);\r\n console.log(`[Estimated time at full resolution] ~${estimatedFullResTime.toFixed(0)}ms`);\r\n console.log(\r\n `[Time saved] ~${(estimatedFullResTime - totalTime).toFixed(0)}ms (${Math.round(estimatedFullResTime / totalTime)}\u00D7 faster)`\r\n );\r\n }\r\n }\r\n\r\n resolve({\r\n imageData: finalImageData,\r\n pngBlob: blob,\r\n });\r\n }, 'image/png');\r\n };\r\n\r\n img.onerror = () => reject(new Error('Failed to load image'));\r\n img.src = typeof file === 'string' ? file : URL.createObjectURL(file);\r\n });\r\n}\r\n\r\nfunction buildSparseData(\r\n shapeMask: Uint8Array,\r\n boundaryMask: Uint8Array,\r\n interiorPixels: Uint32Array,\r\n boundaryPixels: Uint32Array,\r\n width: number,\r\n height: number\r\n): SparsePixelData {\r\n const pixelCount = interiorPixels.length;\r\n\r\n // Build neighbor indices for sparse processing\r\n // For each interior pixel, store indices of its 4 neighbors\r\n // Use -1 for out-of-bounds or non-shape neighbors\r\n const neighborIndices = new Int32Array(pixelCount * 4);\r\n\r\n for (let i = 0; i < pixelCount; i++) {\r\n const idx = interiorPixels[i]!;\r\n const x = idx % width;\r\n const y = Math.floor(idx / width);\r\n\r\n // East neighbor\r\n neighborIndices[i * 4 + 0] = x < width - 1 && shapeMask[idx + 1] ? idx + 1 : -1;\r\n // West neighbor\r\n neighborIndices[i * 4 + 1] = x > 0 && shapeMask[idx - 1] ? idx - 1 : -1;\r\n // North neighbor\r\n neighborIndices[i * 4 + 2] = y > 0 && shapeMask[idx - width] ? idx - width : -1;\r\n // South neighbor\r\n neighborIndices[i * 4 + 3] = y < height - 1 && shapeMask[idx + width] ? idx + width : -1;\r\n }\r\n\r\n return {\r\n interiorPixels,\r\n boundaryPixels,\r\n pixelCount,\r\n neighborIndices,\r\n };\r\n}\r\n\r\nfunction solvePoissonSparse(\r\n sparseData: SparsePixelData,\r\n shapeMask: Uint8Array,\r\n boundaryMask: Uint8Array,\r\n width: number,\r\n height: number\r\n): Float32Array {\r\n // This controls how smooth the falloff roundness will be and extend into the shape\r\n const ITERATIONS = POISSON_CONFIG_OPTIMIZED.iterations;\r\n\r\n // Keep C constant - only iterations control roundness spread\r\n const C = 0.01;\r\n\r\n const u = new Float32Array(width * height);\r\n const { interiorPixels, neighborIndices, pixelCount } = sparseData;\r\n\r\n // Performance tracking\r\n const startTime = performance.now();\r\n\r\n // Red-Black SOR for better symmetry with fewer iterations\r\n // omega between 1.8-1.95 typically gives best convergence for Poisson\r\n const omega = 1.9;\r\n\r\n // Pre-classify pixels as red or black for efficient processing\r\n const redPixels: number[] = [];\r\n const blackPixels: number[] = [];\r\n\r\n for (let i = 0; i < pixelCount; i++) {\r\n const idx = interiorPixels[i]!;\r\n const x = idx % width;\r\n const y = Math.floor(idx / width);\r\n\r\n if ((x + y) % 2 === 0) {\r\n redPixels.push(i);\r\n } else {\r\n blackPixels.push(i);\r\n }\r\n }\r\n\r\n for (let iter = 0; iter < ITERATIONS; iter++) {\r\n // Red pass: update red pixels\r\n for (const i of redPixels) {\r\n const idx = interiorPixels[i]!;\r\n\r\n // Get precomputed neighbor indices\r\n const eastIdx = neighborIndices[i * 4 + 0]!;\r\n const westIdx = neighborIndices[i * 4 + 1]!;\r\n const northIdx = neighborIndices[i * 4 + 2]!;\r\n const southIdx = neighborIndices[i * 4 + 3]!;\r\n\r\n // Sum neighbors (use 0 for out-of-bounds)\r\n let sumN = 0;\r\n if (eastIdx >= 0) sumN += u[eastIdx]!;\r\n if (westIdx >= 0) sumN += u[westIdx]!;\r\n if (northIdx >= 0) sumN += u[northIdx]!;\r\n if (southIdx >= 0) sumN += u[southIdx]!;\r\n\r\n // SOR update: blend new value with old value\r\n const newValue = (C + sumN) / 4;\r\n u[idx] = omega * newValue + (1 - omega) * u[idx]!;\r\n }\r\n\r\n // Black pass: update black pixels\r\n for (const i of blackPixels) {\r\n const idx = interiorPixels[i]!;\r\n\r\n // Get precomputed neighbor indices\r\n const eastIdx = neighborIndices[i * 4 + 0]!;\r\n const westIdx = neighborIndices[i * 4 + 1]!;\r\n const northIdx = neighborIndices[i * 4 + 2]!;\r\n const southIdx = neighborIndices[i * 4 + 3]!;\r\n\r\n // Sum neighbors (use 0 for out-of-bounds)\r\n let sumN = 0;\r\n if (eastIdx >= 0) sumN += u[eastIdx]!;\r\n if (westIdx >= 0) sumN += u[westIdx]!;\r\n if (northIdx >= 0) sumN += u[northIdx]!;\r\n if (southIdx >= 0) sumN += u[southIdx]!;\r\n\r\n // SOR update: blend new value with old value\r\n const newValue = (C + sumN) / 4;\r\n u[idx] = omega * newValue + (1 - omega) * u[idx]!;\r\n }\r\n }\r\n\r\n // Jacobi smoothing passes to remove Red-Black checkerboard artifacts\r\n const tmp = new Float32Array(width * height);\r\n for (let smooth = 0; smooth < 3; smooth++) {\r\n tmp.set(u);\r\n for (let i = 0; i < pixelCount; i++) {\r\n const idx = interiorPixels[i]!;\r\n const eastIdx = neighborIndices[i * 4 + 0]!;\r\n const westIdx = neighborIndices[i * 4 + 1]!;\r\n const northIdx = neighborIndices[i * 4 + 2]!;\r\n const southIdx = neighborIndices[i * 4 + 3]!;\r\n\r\n let sum = 0;\r\n let count = 0;\r\n if (eastIdx >= 0) { sum += tmp[eastIdx]!; count++; }\r\n if (westIdx >= 0) { sum += tmp[westIdx]!; count++; }\r\n if (northIdx >= 0) { sum += tmp[northIdx]!; count++; }\r\n if (southIdx >= 0) { sum += tmp[southIdx]!; count++; }\r\n\r\n u[idx] = count > 0 ? (tmp[idx]! + sum / count) * 0.5 : tmp[idx]!;\r\n }\r\n }\r\n\r\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\r\n const elapsed = performance.now() - startTime;\r\n\r\n console.log(`[Optimized Poisson Solver (SOR \u03C9=${omega})]`);\r\n console.log(` Working size: ${width}\u00D7${height}`);\r\n console.log(` Iterations: ${ITERATIONS}`);\r\n console.log(` Time: ${elapsed.toFixed(2)}ms`);\r\n console.log(` Interior pixels processed: ${pixelCount}`);\r\n console.log(` Speed: ${((ITERATIONS * pixelCount) / (elapsed * 1000)).toFixed(2)} Mpixels/sec`);\r\n }\r\n\r\n return u;\r\n}\r\n\r\nexport interface GemSmokeUniforms extends ShaderSizingUniforms {\r\n u_colorBack: [number, number, number, number];\r\n u_colors: vec4[];\r\n u_colorsCount: number;\r\n u_image: HTMLImageElement | string | undefined;\r\n u_innerDistortion: number;\r\n u_outerDistortion: number;\r\n u_outerGlow: number;\r\n u_innerGlow: number;\r\n u_colorInner: [number, number, number, number];\r\n u_offset: number;\r\n u_angle: number;\r\n u_size: number;\r\n u_shape: (typeof GemSmokeShapes)[GemSmokeShape];\r\n u_isImage: boolean;\r\n}\r\n\r\nexport interface GemSmokeParams extends ShaderSizingParams, ShaderMotionParams {\r\n colors?: string[];\r\n colorBack?: string;\r\n image?: HTMLImageElement | string | undefined;\r\n innerDistortion?: number;\r\n outerDistortion?: number;\r\n outerGlow?: number;\r\n innerGlow?: number;\r\n colorInner?: string;\r\n offset?: number;\r\n angle?: number;\r\n size?: number;\r\n shape?: GemSmokeShape;\r\n}\r\n\r\nexport const GemSmokeShapes = {\r\n none: 0,\r\n circle: 1,\r\n daisy: 2,\r\n diamond: 3,\r\n metaballs: 4,\r\n} as const;\r\n\r\nexport type GemSmokeShape = keyof typeof GemSmokeShapes;\r\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|