@paper-design/shaders 0.0.60 → 0.0.62
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/dist/shader-mount.d.ts +5 -1
- package/dist/shader-mount.js +5 -2
- package/dist/shader-mount.js.map +2 -2
- package/dist/shaders/color-panels.js +4 -3
- package/dist/shaders/color-panels.js.map +2 -2
- package/dist/shaders/dithering.js +4 -4
- package/dist/shaders/dithering.js.map +1 -1
- package/dist/shaders/dot-grid.js +5 -4
- package/dist/shaders/dot-grid.js.map +2 -2
- package/dist/shaders/dot-orbit.js +1 -1
- package/dist/shaders/dot-orbit.js.map +1 -1
- package/dist/shaders/fluted-glass.d.ts +19 -5
- package/dist/shaders/fluted-glass.js +250 -64
- package/dist/shaders/fluted-glass.js.map +2 -2
- package/dist/shaders/god-rays.js +4 -2
- package/dist/shaders/god-rays.js.map +2 -2
- package/dist/shaders/grain-gradient.js +4 -4
- package/dist/shaders/grain-gradient.js.map +1 -1
- package/dist/shaders/heatmap.js +13 -12
- package/dist/shaders/heatmap.js.map +2 -2
- package/dist/shaders/image-dithering.js +2 -2
- package/dist/shaders/image-dithering.js.map +1 -1
- package/dist/shaders/liquid-metal.js +2 -2
- package/dist/shaders/liquid-metal.js.map +1 -1
- package/dist/shaders/mesh-gradient.js +4 -4
- package/dist/shaders/mesh-gradient.js.map +1 -1
- package/dist/shaders/neuro-noise.js +1 -1
- package/dist/shaders/neuro-noise.js.map +1 -1
- package/dist/shaders/paper-texture.js +4 -4
- package/dist/shaders/paper-texture.js.map +1 -1
- package/dist/shaders/perlin-noise.js +2 -2
- package/dist/shaders/perlin-noise.js.map +1 -1
- package/dist/shaders/pulsing-border.js +7 -7
- package/dist/shaders/pulsing-border.js.map +1 -1
- package/dist/shaders/smoke-ring.js +8 -6
- package/dist/shaders/smoke-ring.js.map +2 -2
- package/dist/shaders/spiral.js +1 -1
- package/dist/shaders/spiral.js.map +1 -1
- package/dist/shaders/static-mesh-gradient.js +2 -2
- package/dist/shaders/static-mesh-gradient.js.map +1 -1
- package/dist/shaders/static-radial-gradient.js +17 -14
- package/dist/shaders/static-radial-gradient.js.map +2 -2
- package/dist/shaders/swirl.js +2 -1
- package/dist/shaders/swirl.js.map +2 -2
- package/dist/shaders/warp.js +4 -2
- package/dist/shaders/warp.js.map +2 -2
- package/dist/shaders/water.js +5 -5
- package/dist/shaders/water.js.map +1 -1
- package/dist/shaders/waves.js +3 -1
- package/dist/shaders/waves.js.map +2 -2
- package/package.json +1 -1
package/dist/shaders/heatmap.js
CHANGED
|
@@ -33,9 +33,9 @@ uniform float u_contour;
|
|
|
33
33
|
float getImgFrame(vec2 uv, float th) {
|
|
34
34
|
float frame = 1.;
|
|
35
35
|
frame *= smoothstep(0., th, uv.y);
|
|
36
|
-
frame *= smoothstep(1
|
|
36
|
+
frame *= 1. - smoothstep(1. - th, 1., uv.y);
|
|
37
37
|
frame *= smoothstep(0., th, uv.x);
|
|
38
|
-
frame *= smoothstep(1
|
|
38
|
+
frame *= 1. - smoothstep(1. - th, 1., uv.x);
|
|
39
39
|
return frame;
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -65,7 +65,7 @@ float shadowShape(vec2 uv, float t, float contour) {
|
|
|
65
65
|
|
|
66
66
|
// base shape
|
|
67
67
|
float innerR = .4;
|
|
68
|
-
float outerR = 1. - .3 * (sst(.1, .2, t) * sst(.
|
|
68
|
+
float outerR = 1. - .3 * (sst(.1, .2, t) * (1. - sst(.2, .5, t)));
|
|
69
69
|
float s = circle(scaledUV, vec2(.5, posY - .2), vec2(innerR, outerR));
|
|
70
70
|
float shapeSizing = sst(.2, .3, t) * sst(.6, .3, t);
|
|
71
71
|
s = pow(s, 1.4);
|
|
@@ -76,7 +76,7 @@ float shadowShape(vec2 uv, float t, float contour) {
|
|
|
76
76
|
{
|
|
77
77
|
float pos = posY - uv.y;
|
|
78
78
|
float edge = 1.2;
|
|
79
|
-
topFlattener = lst(-.4, 0., pos) * sst(
|
|
79
|
+
topFlattener = lst(-.4, 0., pos) * (1. - sst(.0, edge, pos));
|
|
80
80
|
topFlattener = pow(topFlattener, 3.);
|
|
81
81
|
float topFlattenerMixer = (1. - sst(.0, .3, pos));
|
|
82
82
|
s = mix(topFlattener, s, topFlattenerMixer);
|
|
@@ -84,7 +84,7 @@ float shadowShape(vec2 uv, float t, float contour) {
|
|
|
84
84
|
|
|
85
85
|
// apple right circle
|
|
86
86
|
{
|
|
87
|
-
float visibility = sst(.6, .7, t) * sst(.
|
|
87
|
+
float visibility = sst(.6, .7, t) * (1. - sst(.8, .9, t));
|
|
88
88
|
float angle = -2. -t * TWO_PI;
|
|
89
89
|
float rightCircle = circle(uv, vec2(.95 - .2 * cos(angle), .4 - .1 * sin(angle)), vec2(.15, .3));
|
|
90
90
|
rightCircle *= visibility;
|
|
@@ -95,31 +95,32 @@ float shadowShape(vec2 uv, float t, float contour) {
|
|
|
95
95
|
{
|
|
96
96
|
float topCircle = circle(uv, vec2(.5, .19), vec2(.05, .25));
|
|
97
97
|
topCircle += 2. * contour * circle(uv, vec2(.5, .19), vec2(.2, .5));
|
|
98
|
-
float visibility = .55 * sst(.2, .3, t) * sst(.
|
|
98
|
+
float visibility = .55 * sst(.2, .3, t) * (1. - sst(.3, .45, t));
|
|
99
99
|
topCircle *= visibility;
|
|
100
100
|
s = mix(s, 0., topCircle);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
float leafMask = circle(uv, vec2(.53, .13), vec2(.08, .19));
|
|
104
|
-
leafMask = mix(leafMask, 0., sst(.
|
|
104
|
+
leafMask = mix(leafMask, 0., 1. - sst(.4, .54, uv.x));
|
|
105
105
|
leafMask = mix(0., leafMask, sst(.0, .2, uv.y));
|
|
106
106
|
leafMask *= (sst(.5, 1.1, posY) * sst(1.5, 1.3, posY));
|
|
107
107
|
s += leafMask;
|
|
108
108
|
|
|
109
109
|
// apple bottom circle
|
|
110
110
|
{
|
|
111
|
-
float visibility = sst(.0, .4, t) * sst(.
|
|
111
|
+
float visibility = sst(.0, .4, t) * (1. - sst(.6, .8, t));
|
|
112
112
|
s = mix(s, 0., visibility * circle(uv, vec2(.52, .92), vec2(.09, .25)));
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
// random balls that are invisible if apple logo is selected
|
|
116
116
|
{
|
|
117
|
-
float pos = sst(.0, .6, t) *
|
|
117
|
+
float pos = sst(.0, .6, t) * (1. - sst(.6, 1., t));
|
|
118
118
|
s = mix(s, .5, circle(uv, vec2(.0, 1.2 - .5 * pos), vec2(.1, .3)));
|
|
119
119
|
s = mix(s, .0, circle(uv, vec2(1., .5 + .5 * pos), vec2(.1, .3)));
|
|
120
120
|
|
|
121
121
|
s = mix(s, 1., circle(uv, vec2(.95, .2 + .2 * sst(.3, .4, t) * sst(.7, .5, t)), vec2(.07, .22)));
|
|
122
|
-
s
|
|
122
|
+
s = mix(s, 1., circle(uv, vec2(.95, .2 + .2 * sst(.3, .4, t) * (1. - sst(.5, .7, t))), vec2(.07, .22)));
|
|
123
|
+
s /= max(1e-4, sst(1., .85, uv.y));
|
|
123
124
|
}
|
|
124
125
|
|
|
125
126
|
s = clamp(0., 1., s);
|
|
@@ -154,7 +155,7 @@ void main() {
|
|
|
154
155
|
tCopy2 = mod(tCopy2, 1.);
|
|
155
156
|
|
|
156
157
|
vec2 animationUV = imgUV - vec2(.5);
|
|
157
|
-
float angle = u_angle * PI / 180.;
|
|
158
|
+
float angle = -u_angle * PI / 180.;
|
|
158
159
|
float cosA = cos(angle);
|
|
159
160
|
float sinA = sin(angle);
|
|
160
161
|
animationUV = vec2(
|
|
@@ -191,7 +192,7 @@ void main() {
|
|
|
191
192
|
|
|
192
193
|
outer = .9 * pow(outerBlur, .8);
|
|
193
194
|
float y = mod(animationUV.y - t, 1.);
|
|
194
|
-
float animatedMask = sst(.3, .65, y) *
|
|
195
|
+
float animatedMask = sst(.3, .65, y) * (1. - sst(.65, 1., y));
|
|
195
196
|
animatedMask = .5 + animatedMask;
|
|
196
197
|
outer *= animatedMask;
|
|
197
198
|
outer *= mix(0., 5., pow(u_outerGlow, 2.));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/shaders/heatmap.ts"],
|
|
4
|
-
"sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport type { ShaderSizingParams, ShaderSizingUniforms } from '../shader-sizing.js';\n\nexport const heatmapMeta = {\n maxColorCount: 10,\n} as const;\n\n// language=GLSL\nexport const heatmapFragmentShader: string = `#version 300 es\nprecision highp float;\n\nin mediump vec2 v_imageUV;\nin mediump vec2 v_objectUV;\nout vec4 fragColor;\n\nuniform sampler2D u_image;\nuniform float u_time;\nuniform mediump float u_imageAspectRatio;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colors[${heatmapMeta.maxColorCount}];\nuniform float u_colorsCount;\n\nuniform float u_angle;\nuniform float u_noise;\nuniform float u_innerGlow;\nuniform float u_outerGlow;\nuniform float u_contour;\n\n#define TWO_PI 6.28318530718\n#define PI 3.14159265358979323846\n\nfloat getImgFrame(vec2 uv, float th) {\n float frame = 1.;\n frame *= smoothstep(0., th, uv.y);\n frame *= smoothstep(1., 1. - th, uv.y);\n frame *= smoothstep(0., th, uv.x);\n frame *= smoothstep(1., 1. - th, uv.x);\n return frame;\n}\n\nfloat circle(vec2 uv, vec2 c, vec2 r) {\n return 1. - smoothstep(r[0], r[1], length(uv - c));\n}\n\nfloat lst(float edge0, float edge1, float x) {\n return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);\n}\n\nfloat sst(float edge0, float edge1, float x) {\n return smoothstep(edge0, edge1, x);\n}\n\nfloat shadowShape(vec2 uv, float t, float contour) {\n vec2 scaledUV = uv;\n\n // base shape tranjectory\n float posY = mix(-1., 2., t);\n\n // scaleX when it's moving down\n scaledUV.y -= .5;\n float mainCircleScale = sst(0., .8, posY) * lst(1.4, .9, posY);\n scaledUV *= vec2(1., 1. + 1.5 * mainCircleScale);\n scaledUV.y += .5;\n\n // base shape\n float innerR = .4;\n float outerR = 1. - .3 * (sst(.1, .2, t) * sst(.5, .2, t));\n float s = circle(scaledUV, vec2(.5, posY - .2), vec2(innerR, outerR));\n float shapeSizing = sst(.2, .3, t) * sst(.6, .3, t);\n s = pow(s, 1.4);\n s *= 1.2;\n\n // flat gradient to take over the shadow shape\n float topFlattener = 0.;\n {\n float pos = posY - uv.y;\n float edge = 1.2;\n topFlattener = lst(-.4, 0., pos) * sst(edge, .0, pos);\n topFlattener = pow(topFlattener, 3.);\n float topFlattenerMixer = (1. - sst(.0, .3, pos));\n s = mix(topFlattener, s, topFlattenerMixer);\n }\n\n // apple right circle\n {\n float visibility = sst(.6, .7, t) * sst(.9, .8, t);\n float angle = -2. -t * TWO_PI;\n float rightCircle = circle(uv, vec2(.95 - .2 * cos(angle), .4 - .1 * sin(angle)), vec2(.15, .3));\n rightCircle *= visibility;\n s = mix(s, 0., rightCircle);\n }\n\n // apple top circle\n {\n float topCircle = circle(uv, vec2(.5, .19), vec2(.05, .25));\n topCircle += 2. * contour * circle(uv, vec2(.5, .19), vec2(.2, .5));\n float visibility = .55 * sst(.2, .3, t) * sst(.45, .3, t);\n topCircle *= visibility;\n s = mix(s, 0., topCircle);\n }\n\n float leafMask = circle(uv, vec2(.53, .13), vec2(.08, .19));\n leafMask = mix(leafMask, 0., sst(.54, .4, uv.x));\n leafMask = mix(0., leafMask, sst(.0, .2, uv.y));\n leafMask *= (sst(.5, 1.1, posY) * sst(1.5, 1.3, posY));\n s += leafMask;\n\n // apple bottom circle\n {\n float visibility = sst(.0, .4, t) * sst(.8, .6, t);\n s = mix(s, 0., visibility * circle(uv, vec2(.52, .92), vec2(.09, .25)));\n }\n\n // random balls that are invisible if apple logo is selected\n {\n float pos = sst(.0, .6, t) * sst(1., .6, t);\n s = mix(s, .5, circle(uv, vec2(.0, 1.2 - .5 * pos), vec2(.1, .3)));\n s = mix(s, .0, circle(uv, vec2(1., .5 + .5 * pos), vec2(.1, .3)));\n\n s = mix(s, 1., circle(uv, vec2(.95, .2 + .2 * sst(.3, .4, t) * sst(.7, .5, t)), vec2(.07, .22)));\n s /= sst(1., .85, uv.y);\n }\n\n s = clamp(0., 1., s);\n return s;\n}\n\n\nvoid main() {\n vec2 uv = v_objectUV + .5;\n uv.y = 1. - uv.y;\n\n vec2 imgUV = v_imageUV;\n imgUV -= .5;\n imgUV *= 0.5714285714285714;\n imgUV += .5;\n float imgSoftFrame = getImgFrame(imgUV, .03);\n\n vec4 img = texture(u_image, imgUV);\n if (img.a == 0.) {\n fragColor = u_colorBack;\n return;\n }\n\n float t = .1 * u_time;\n t -= .3;\n\n float tCopy = t + 1. / 3.;\n float tCopy2 = t + 2. / 3.;\n\n t = mod(t, 1.);\n tCopy = mod(tCopy, 1.);\n tCopy2 = mod(tCopy2, 1.);\n\n vec2 animationUV = imgUV - vec2(.5);\n float angle = u_angle * PI / 180.;\n float cosA = cos(angle);\n float sinA = sin(angle);\n animationUV = vec2(\n animationUV.x * cosA - animationUV.y * sinA,\n animationUV.x * sinA + animationUV.y * cosA\n ) + vec2(.5);\n\n float shape = img[0];\n float outerBlur = 1. - mix(1., img[1], shape);\n float innerBlur = mix(img[1], 0., shape);\n float contour = mix(img[2], 0., shape);\n\n outerBlur *= imgSoftFrame;\n\n float shadow = shadowShape(animationUV, t, innerBlur);\n float shadowCopy = shadowShape(animationUV, tCopy, innerBlur);\n float shadowCopy2 = shadowShape(animationUV, tCopy2, innerBlur);\n\n float inner = .8 + .8 * innerBlur;\n inner = mix(inner, 0., shadow);\n inner = mix(inner, 0., shadowCopy);\n inner = mix(inner, 0., shadowCopy2);\n\n inner *= mix(0., 2., u_innerGlow);\n\n inner += (u_contour * 2.) * contour;\n inner = min(1., inner);\n inner *= (1. - shape);\n\n float outer = 0.;\n {\n t *= 3.;\n t = mod(t - .1, 1.);\n\n outer = .9 * pow(outerBlur, .8);\n float y = mod(animationUV.y - t, 1.);\n float animatedMask = sst(.3, .65, y) * sst(1., .65, y);\n animatedMask = .5 + animatedMask;\n outer *= animatedMask;\n outer *= mix(0., 5., pow(u_outerGlow, 2.));\n outer *= imgSoftFrame;\n }\n\n inner = pow(inner, 1.2);\n float heat = clamp(inner + outer, 0., 1.);\n\n heat += (.005 + .35 * u_noise) * (fract(sin(dot(uv, vec2(12.9898, 78.233))) * 43758.5453123) - .5);\n\n float mixer = heat * u_colorsCount;\n vec4 gradient = u_colors[0];\n gradient.rgb *= gradient.a;\n float outerShape = 0.;\n for (int i = 1; i < ${heatmapMeta.maxColorCount + 1}; i++) {\n if (i > int(u_colorsCount)) break;\n float m = clamp(mixer - float(i - 1), 0., 1.);\n if (i == 1) {\n outerShape = m;\n }\n vec4 c = u_colors[i - 1];\n c.rgb *= c.a;\n gradient = mix(gradient, c, m);\n }\n\n vec3 color = gradient.rgb * outerShape;\n float opacity = gradient.a * outerShape;\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n color = color + bgColor * (1.0 - opacity);\n opacity = opacity + u_colorBack.a * (1.0 - opacity);\n\n color += .02 * (fract(sin(dot(uv + 1., vec2(12.9898, 78.233))) * 43758.5453123) - .5);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport function toProcessedHeatmap(file: File | string): Promise<{ blob: Blob }> {\n const canvas = document.createElement('canvas');\n const canvasSize = 1000;\n\n return new Promise((resolve, reject) => {\n const image = new Image();\n image.crossOrigin = 'anonymous';\n\n image.addEventListener('load', () => {\n if (typeof file === 'string' ? file.endsWith('.svg') : file.type === 'image/svg+xml') {\n // Force SVG to load at a high fidelity size if it's an SVG\n image.width = canvasSize;\n image.height = canvasSize;\n }\n\n const ratio = image.naturalWidth / image.naturalHeight;\n\n const maxBlur = Math.floor(canvasSize * 0.15);\n const padding = Math.ceil(maxBlur * 2.5);\n let imgWidth = canvasSize;\n let imgHeight = canvasSize;\n if (ratio > 1) {\n imgHeight = Math.floor(canvasSize / ratio);\n } else {\n imgWidth = Math.floor(canvasSize * ratio);\n }\n\n canvas.width = imgWidth + 2 * padding;\n canvas.height = imgHeight + 2 * padding;\n\n const ctx = canvas.getContext('2d', { willReadFrequently: true });\n if (!ctx) {\n throw new Error('Failed to get canvas 2d context');\n }\n\n ctx.fillStyle = 'white';\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n ctx.filter = 'grayscale(100%) blur(' + maxBlur + 'px)';\n ctx.drawImage(image, padding, padding, imgWidth, imgHeight);\n const bigBlurData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;\n\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n ctx.filter = 'grayscale(100%) blur(' + Math.round(0.12 * maxBlur) + 'px)';\n ctx.drawImage(image, padding, padding, imgWidth, imgHeight);\n const innerBlurSmallData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;\n\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n ctx.filter = 'grayscale(100%) blur(5px)';\n ctx.drawImage(image, padding, padding, imgWidth, imgHeight);\n const contourData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;\n\n let processedImageData = ctx.createImageData(canvas.width, canvas.height);\n const totalPixels = canvas.width * canvas.height;\n for (let i = 0; i < totalPixels; i++) {\n const px = i * 4;\n processedImageData.data[px] = contourData[px]!;\n processedImageData.data[px + 1] = bigBlurData[px]!;\n processedImageData.data[px + 2] = innerBlurSmallData[px]!;\n processedImageData.data[px + 3] = 255;\n }\n ctx.putImageData(processedImageData, 0, 0);\n\n canvas.toBlob((blob) => {\n if (!blob) {\n reject(new Error('Failed to create PNG blob'));\n return;\n }\n\n resolve({ blob });\n }, 'image/png');\n });\n\n image.addEventListener('error', () => {\n reject(new Error('Failed to load image'));\n });\n\n image.src = typeof file === 'string' ? file : URL.createObjectURL(file);\n });\n}\n\nexport interface HeatmapUniforms extends ShaderSizingUniforms {\n u_image: HTMLImageElement | string;\n u_contour: number;\n u_angle: number;\n u_noise: number;\n u_innerGlow: number;\n u_outerGlow: number;\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n}\n\nexport interface HeatmapParams extends ShaderSizingParams, ShaderMotionParams {\n image: HTMLImageElement | string;\n contour?: number;\n angle?: number;\n noise?: number;\n innerGlow?: number;\n outerGlow?: number;\n colorBack?: string;\n colors?: string[];\n}\n"],
|
|
5
|
-
"mappings": ";;;;;AAIO,MAAM,cAAc;AAAA,EACzB,eAAe;AACjB;AAGO,MAAM,wBAAgC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAYrB,YAAY,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,
|
|
4
|
+
"sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport type { ShaderSizingParams, ShaderSizingUniforms } from '../shader-sizing.js';\n\nexport const heatmapMeta = {\n maxColorCount: 10,\n} as const;\n\n// language=GLSL\nexport const heatmapFragmentShader: string = `#version 300 es\nprecision highp float;\n\nin mediump vec2 v_imageUV;\nin mediump vec2 v_objectUV;\nout vec4 fragColor;\n\nuniform sampler2D u_image;\nuniform float u_time;\nuniform mediump float u_imageAspectRatio;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colors[${heatmapMeta.maxColorCount}];\nuniform float u_colorsCount;\n\nuniform float u_angle;\nuniform float u_noise;\nuniform float u_innerGlow;\nuniform float u_outerGlow;\nuniform float u_contour;\n\n#define TWO_PI 6.28318530718\n#define PI 3.14159265358979323846\n\nfloat getImgFrame(vec2 uv, float th) {\n float frame = 1.;\n frame *= smoothstep(0., th, uv.y);\n frame *= 1. - smoothstep(1. - th, 1., uv.y);\n frame *= smoothstep(0., th, uv.x);\n frame *= 1. - smoothstep(1. - th, 1., uv.x);\n return frame;\n}\n\nfloat circle(vec2 uv, vec2 c, vec2 r) {\n return 1. - smoothstep(r[0], r[1], length(uv - c));\n}\n\nfloat lst(float edge0, float edge1, float x) {\n return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);\n}\n\nfloat sst(float edge0, float edge1, float x) {\n return smoothstep(edge0, edge1, x);\n}\n\nfloat shadowShape(vec2 uv, float t, float contour) {\n vec2 scaledUV = uv;\n\n // base shape tranjectory\n float posY = mix(-1., 2., t);\n\n // scaleX when it's moving down\n scaledUV.y -= .5;\n float mainCircleScale = sst(0., .8, posY) * lst(1.4, .9, posY);\n scaledUV *= vec2(1., 1. + 1.5 * mainCircleScale);\n scaledUV.y += .5;\n\n // base shape\n float innerR = .4;\n float outerR = 1. - .3 * (sst(.1, .2, t) * (1. - sst(.2, .5, t)));\n float s = circle(scaledUV, vec2(.5, posY - .2), vec2(innerR, outerR));\n float shapeSizing = sst(.2, .3, t) * sst(.6, .3, t);\n s = pow(s, 1.4);\n s *= 1.2;\n\n // flat gradient to take over the shadow shape\n float topFlattener = 0.;\n {\n float pos = posY - uv.y;\n float edge = 1.2;\n topFlattener = lst(-.4, 0., pos) * (1. - sst(.0, edge, pos));\n topFlattener = pow(topFlattener, 3.);\n float topFlattenerMixer = (1. - sst(.0, .3, pos));\n s = mix(topFlattener, s, topFlattenerMixer);\n }\n\n // apple right circle\n {\n float visibility = sst(.6, .7, t) * (1. - sst(.8, .9, t));\n float angle = -2. -t * TWO_PI;\n float rightCircle = circle(uv, vec2(.95 - .2 * cos(angle), .4 - .1 * sin(angle)), vec2(.15, .3));\n rightCircle *= visibility;\n s = mix(s, 0., rightCircle);\n }\n\n // apple top circle\n {\n float topCircle = circle(uv, vec2(.5, .19), vec2(.05, .25));\n topCircle += 2. * contour * circle(uv, vec2(.5, .19), vec2(.2, .5));\n float visibility = .55 * sst(.2, .3, t) * (1. - sst(.3, .45, t));\n topCircle *= visibility;\n s = mix(s, 0., topCircle);\n }\n\n float leafMask = circle(uv, vec2(.53, .13), vec2(.08, .19));\n leafMask = mix(leafMask, 0., 1. - sst(.4, .54, uv.x));\n leafMask = mix(0., leafMask, sst(.0, .2, uv.y));\n leafMask *= (sst(.5, 1.1, posY) * sst(1.5, 1.3, posY));\n s += leafMask;\n\n // apple bottom circle\n {\n float visibility = sst(.0, .4, t) * (1. - sst(.6, .8, t));\n s = mix(s, 0., visibility * circle(uv, vec2(.52, .92), vec2(.09, .25)));\n }\n\n // random balls that are invisible if apple logo is selected\n {\n float pos = sst(.0, .6, t) * (1. - sst(.6, 1., t));\n s = mix(s, .5, circle(uv, vec2(.0, 1.2 - .5 * pos), vec2(.1, .3)));\n s = mix(s, .0, circle(uv, vec2(1., .5 + .5 * pos), vec2(.1, .3)));\n\n s = mix(s, 1., circle(uv, vec2(.95, .2 + .2 * sst(.3, .4, t) * sst(.7, .5, t)), vec2(.07, .22)));\n s = mix(s, 1., circle(uv, vec2(.95, .2 + .2 * sst(.3, .4, t) * (1. - sst(.5, .7, t))), vec2(.07, .22)));\n s /= max(1e-4, sst(1., .85, uv.y));\n }\n\n s = clamp(0., 1., s);\n return s;\n}\n\n\nvoid main() {\n vec2 uv = v_objectUV + .5;\n uv.y = 1. - uv.y;\n\n vec2 imgUV = v_imageUV;\n imgUV -= .5;\n imgUV *= 0.5714285714285714;\n imgUV += .5;\n float imgSoftFrame = getImgFrame(imgUV, .03);\n\n vec4 img = texture(u_image, imgUV);\n if (img.a == 0.) {\n fragColor = u_colorBack;\n return;\n }\n\n float t = .1 * u_time;\n t -= .3;\n\n float tCopy = t + 1. / 3.;\n float tCopy2 = t + 2. / 3.;\n\n t = mod(t, 1.);\n tCopy = mod(tCopy, 1.);\n tCopy2 = mod(tCopy2, 1.);\n\n vec2 animationUV = imgUV - vec2(.5);\n float angle = -u_angle * PI / 180.;\n float cosA = cos(angle);\n float sinA = sin(angle);\n animationUV = vec2(\n animationUV.x * cosA - animationUV.y * sinA,\n animationUV.x * sinA + animationUV.y * cosA\n ) + vec2(.5);\n\n float shape = img[0];\n float outerBlur = 1. - mix(1., img[1], shape);\n float innerBlur = mix(img[1], 0., shape);\n float contour = mix(img[2], 0., shape);\n\n outerBlur *= imgSoftFrame;\n\n float shadow = shadowShape(animationUV, t, innerBlur);\n float shadowCopy = shadowShape(animationUV, tCopy, innerBlur);\n float shadowCopy2 = shadowShape(animationUV, tCopy2, innerBlur);\n\n float inner = .8 + .8 * innerBlur;\n inner = mix(inner, 0., shadow);\n inner = mix(inner, 0., shadowCopy);\n inner = mix(inner, 0., shadowCopy2);\n\n inner *= mix(0., 2., u_innerGlow);\n\n inner += (u_contour * 2.) * contour;\n inner = min(1., inner);\n inner *= (1. - shape);\n\n float outer = 0.;\n {\n t *= 3.;\n t = mod(t - .1, 1.);\n\n outer = .9 * pow(outerBlur, .8);\n float y = mod(animationUV.y - t, 1.);\n float animatedMask = sst(.3, .65, y) * (1. - sst(.65, 1., y));\n animatedMask = .5 + animatedMask;\n outer *= animatedMask;\n outer *= mix(0., 5., pow(u_outerGlow, 2.));\n outer *= imgSoftFrame;\n }\n\n inner = pow(inner, 1.2);\n float heat = clamp(inner + outer, 0., 1.);\n\n heat += (.005 + .35 * u_noise) * (fract(sin(dot(uv, vec2(12.9898, 78.233))) * 43758.5453123) - .5);\n\n float mixer = heat * u_colorsCount;\n vec4 gradient = u_colors[0];\n gradient.rgb *= gradient.a;\n float outerShape = 0.;\n for (int i = 1; i < ${heatmapMeta.maxColorCount + 1}; i++) {\n if (i > int(u_colorsCount)) break;\n float m = clamp(mixer - float(i - 1), 0., 1.);\n if (i == 1) {\n outerShape = m;\n }\n vec4 c = u_colors[i - 1];\n c.rgb *= c.a;\n gradient = mix(gradient, c, m);\n }\n\n vec3 color = gradient.rgb * outerShape;\n float opacity = gradient.a * outerShape;\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n color = color + bgColor * (1.0 - opacity);\n opacity = opacity + u_colorBack.a * (1.0 - opacity);\n\n color += .02 * (fract(sin(dot(uv + 1., vec2(12.9898, 78.233))) * 43758.5453123) - .5);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport function toProcessedHeatmap(file: File | string): Promise<{ blob: Blob }> {\n const canvas = document.createElement('canvas');\n const canvasSize = 1000;\n\n return new Promise((resolve, reject) => {\n const image = new Image();\n image.crossOrigin = 'anonymous';\n\n image.addEventListener('load', () => {\n if (typeof file === 'string' ? file.endsWith('.svg') : file.type === 'image/svg+xml') {\n // Force SVG to load at a high fidelity size if it's an SVG\n image.width = canvasSize;\n image.height = canvasSize;\n }\n\n const ratio = image.naturalWidth / image.naturalHeight;\n\n const maxBlur = Math.floor(canvasSize * 0.15);\n const padding = Math.ceil(maxBlur * 2.5);\n let imgWidth = canvasSize;\n let imgHeight = canvasSize;\n if (ratio > 1) {\n imgHeight = Math.floor(canvasSize / ratio);\n } else {\n imgWidth = Math.floor(canvasSize * ratio);\n }\n\n canvas.width = imgWidth + 2 * padding;\n canvas.height = imgHeight + 2 * padding;\n\n const ctx = canvas.getContext('2d', { willReadFrequently: true });\n if (!ctx) {\n throw new Error('Failed to get canvas 2d context');\n }\n\n ctx.fillStyle = 'white';\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n ctx.filter = 'grayscale(100%) blur(' + maxBlur + 'px)';\n ctx.drawImage(image, padding, padding, imgWidth, imgHeight);\n const bigBlurData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;\n\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n ctx.filter = 'grayscale(100%) blur(' + Math.round(0.12 * maxBlur) + 'px)';\n ctx.drawImage(image, padding, padding, imgWidth, imgHeight);\n const innerBlurSmallData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;\n\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n ctx.filter = 'grayscale(100%) blur(5px)';\n ctx.drawImage(image, padding, padding, imgWidth, imgHeight);\n const contourData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;\n\n let processedImageData = ctx.createImageData(canvas.width, canvas.height);\n const totalPixels = canvas.width * canvas.height;\n for (let i = 0; i < totalPixels; i++) {\n const px = i * 4;\n processedImageData.data[px] = contourData[px]!;\n processedImageData.data[px + 1] = bigBlurData[px]!;\n processedImageData.data[px + 2] = innerBlurSmallData[px]!;\n processedImageData.data[px + 3] = 255;\n }\n ctx.putImageData(processedImageData, 0, 0);\n\n canvas.toBlob((blob) => {\n if (!blob) {\n reject(new Error('Failed to create PNG blob'));\n return;\n }\n\n resolve({ blob });\n }, 'image/png');\n });\n\n image.addEventListener('error', () => {\n reject(new Error('Failed to load image'));\n });\n\n image.src = typeof file === 'string' ? file : URL.createObjectURL(file);\n });\n}\n\nexport interface HeatmapUniforms extends ShaderSizingUniforms {\n u_image: HTMLImageElement | string;\n u_contour: number;\n u_angle: number;\n u_noise: number;\n u_innerGlow: number;\n u_outerGlow: number;\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n}\n\nexport interface HeatmapParams extends ShaderSizingParams, ShaderMotionParams {\n image: HTMLImageElement | string;\n contour?: number;\n angle?: number;\n noise?: number;\n innerGlow?: number;\n outerGlow?: number;\n colorBack?: string;\n colors?: string[];\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { sizingUV } from "../shader-sizing.js";
|
|
7
7
|
import { proceduralHash21 } from "../shader-utils.js";
|
|
8
8
|
const imageDitheringFragmentShader = `#version 300 es
|
|
9
|
-
precision
|
|
9
|
+
precision mediump float;
|
|
10
10
|
|
|
11
11
|
uniform mediump vec2 u_resolution;
|
|
12
12
|
uniform mediump float u_pixelRatio;
|
|
@@ -66,7 +66,7 @@ const int bayer8x8[64] = int[64](
|
|
|
66
66
|
);
|
|
67
67
|
|
|
68
68
|
float getBayerValue(vec2 uv, int size) {
|
|
69
|
-
ivec2 pos = ivec2(
|
|
69
|
+
ivec2 pos = ivec2(fract(uv / float(size)) * float(size));
|
|
70
70
|
int index = pos.y * size + pos.x;
|
|
71
71
|
|
|
72
72
|
if (size == 2) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/shaders/image-dithering.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingUV, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { proceduralHash21 } from '../shader-utils.js';\n\n/**\n * Dithering effect over user texture using 3-color palette\n * or colors sampled from original image\n *\n * Uniforms:\n * - u_colorBack, u_colorFront, u_colorHighlight (RGBA)\n * (u_colorHighlight to be the lightest parts of u_colorFront pixels)\n * - pxSize: px size set relative to canvas resolution\n * - u_type (float used as integer)\n * ---- 1: random dithering\n * ---- 2: 2x2 Bayer matrix\n * ---- 3: 4x4 Bayer matrix\n * ---- 4: 8x8 Bayer matrix\n * - u_originalColors - switching between 3 colors palette and original image colors\n * - u_colorSteps - number of colors to use (applies to both color modes)\n *\n * Note: pixelization is applied to the shapes BEFORE dithering,\n * meaning pixels don't react to scaling and fit\n */\n\n// language=GLSL\nexport const imageDitheringFragmentShader: string = `#version 300 es\nprecision
|
|
4
|
+
"sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingUV, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { proceduralHash21 } from '../shader-utils.js';\n\n/**\n * Dithering effect over user texture using 3-color palette\n * or colors sampled from original image\n *\n * Uniforms:\n * - u_colorBack, u_colorFront, u_colorHighlight (RGBA)\n * (u_colorHighlight to be the lightest parts of u_colorFront pixels)\n * - pxSize: px size set relative to canvas resolution\n * - u_type (float used as integer)\n * ---- 1: random dithering\n * ---- 2: 2x2 Bayer matrix\n * ---- 3: 4x4 Bayer matrix\n * ---- 4: 8x8 Bayer matrix\n * - u_originalColors - switching between 3 colors palette and original image colors\n * - u_colorSteps - number of colors to use (applies to both color modes)\n *\n * Note: pixelization is applied to the shapes BEFORE dithering,\n * meaning pixels don't react to scaling and fit\n */\n\n// language=GLSL\nexport const imageDitheringFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform mediump vec2 u_resolution;\nuniform mediump float u_pixelRatio;\nuniform mediump float u_originX;\nuniform mediump float u_originY;\nuniform mediump float u_worldWidth;\nuniform mediump float u_worldHeight;\nuniform mediump float u_fit;\n\nuniform mediump float u_scale;\nuniform mediump float u_rotation;\nuniform mediump float u_offsetX;\nuniform mediump float u_offsetY;\n\nuniform vec4 u_colorFront;\nuniform vec4 u_colorBack;\nuniform vec4 u_colorHighlight;\n\nuniform sampler2D u_image;\nuniform mediump float u_imageAspectRatio;\n\nuniform float u_type;\nuniform float u_pxSize;\nuniform bool u_originalColors;\nuniform float u_colorSteps;\n\nout vec4 fragColor;\n\nfloat getUvFrame(vec2 uv, vec2 px) {\n float left = step(-px.x, uv.x);\n float right = step(uv.x, 1.);\n float bottom = step(-px.y, uv.y);\n float top = step(uv.y, 1. + px.y);\n\n return left * right * bottom * top;\n}\n\n${proceduralHash21}\n\nconst int bayer2x2[4] = int[4](0, 2, 3, 1);\nconst int bayer4x4[16] = int[16](\n 0, 8, 2, 10,\n 12, 4, 14, 6,\n 3, 11, 1, 9,\n 15, 7, 13, 5\n);\n\nconst int bayer8x8[64] = int[64](\n 0, 32, 8, 40, 2, 34, 10, 42,\n 48, 16, 56, 24, 50, 18, 58, 26,\n 12, 44, 4, 36, 14, 46, 6, 38,\n 60, 28, 52, 20, 62, 30, 54, 22,\n 3, 35, 11, 43, 1, 33, 9, 41,\n 51, 19, 59, 27, 49, 17, 57, 25,\n 15, 47, 7, 39, 13, 45, 5, 37,\n 63, 31, 55, 23, 61, 29, 53, 21\n);\n\nfloat getBayerValue(vec2 uv, int size) {\n ivec2 pos = ivec2(fract(uv / float(size)) * float(size));\n int index = pos.y * size + pos.x;\n\n if (size == 2) {\n return float(bayer2x2[index]) / 4.0;\n } else if (size == 4) {\n return float(bayer4x4[index]) / 16.0;\n } else if (size == 8) {\n return float(bayer8x8[index]) / 64.0;\n }\n return 0.0;\n}\n\n\nvoid main() {\n\n #define USE_IMAGE_SIZING\n #define USE_PIXELIZATION\n ${sizingUV}\n\n vec2 dithering_uv = pxSizeUv;\n vec2 ditheringNoise_uv = u_resolution * uv;\n vec4 image = texture(u_image, imageUV);\n float frame = getUvFrame(imageUV, pxSize / u_resolution.xy);\n\n int type = int(floor(u_type));\n float dithering = 0.0;\n\n float lum = dot(vec3(.2126, .7152, .0722), image.rgb);\n\n switch (type) {\n case 1: {\n dithering = step(hash21(ditheringNoise_uv), lum);\n } break;\n case 2:\n dithering = getBayerValue(dithering_uv, 2);\n break;\n case 3:\n dithering = getBayerValue(dithering_uv, 4);\n break;\n default:\n dithering = getBayerValue(dithering_uv, 8);\n break;\n }\n\n\n float steps = max(floor(u_colorSteps), 1.);\n float ditherAmount = 1.0 / (steps);\n\n vec3 color = vec3(0.0);\n float opacity = 1.;\n\n dithering -= .5;\n float brightness = clamp(lum + dithering * ditherAmount, 0.0, 1.0);\n brightness = mix(0.0, brightness, frame);\n float quantLum = floor(brightness * steps + 0.5) / steps;\n\n if (u_originalColors == true) {\n vec3 normColor = image.rgb / max(lum, 0.001);\n color = normColor * quantLum;\n\n float quantAlpha = floor(image.a * steps + 0.5) / steps;\n opacity = mix(quantLum, 1., quantAlpha);\n } else {\n vec3 fgColor = u_colorFront.rgb * u_colorFront.a;\n float fgOpacity = u_colorFront.a;\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n float bgOpacity = u_colorBack.a;\n vec3 hlColor = u_colorHighlight.rgb * u_colorHighlight.a;\n float hlOpacity = u_colorHighlight.a;\n\n fgColor = mix(fgColor, hlColor, step(1.02 - .02 * u_colorSteps, brightness));\n fgOpacity = mix(fgOpacity, hlOpacity, step(1.02 - .02 * u_colorSteps, brightness));\n\n color = fgColor * quantLum;\n opacity = fgOpacity * quantLum;\n color += bgColor * (1.0 - opacity);\n opacity += bgOpacity * (1.0 - opacity);\n }\n\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface ImageDitheringUniforms extends ShaderSizingUniforms {\n u_image: HTMLImageElement | string;\n u_colorFront: [number, number, number, number];\n u_colorBack: [number, number, number, number];\n u_colorHighlight: [number, number, number, number];\n u_type: (typeof DitheringTypes)[DitheringType];\n u_pxSize: number;\n u_colorSteps: number;\n u_originalColors: boolean;\n}\n\nexport interface ImageDitheringParams extends ShaderSizingParams, ShaderMotionParams {\n image: HTMLImageElement | string;\n colorFront?: string;\n colorBack?: string;\n colorHighlight?: string;\n type?: DitheringType;\n size?: number;\n colorSteps?: number;\n originalColors?: boolean;\n}\n\nexport const DitheringTypes = {\n 'random': 1,\n '2x2': 2,\n '4x4': 3,\n '8x8': 4,\n} as const;\n\nexport type DitheringType = keyof typeof DitheringTypes;\n"],
|
|
5
5
|
"mappings": ";;;;;AACA,SAAS,gBAAoE;AAC7E,SAAS,wBAAwB;AAuB1B,MAAM,+BAAuC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuClD,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAwCd,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyFL,MAAM,iBAAiB;AAAA,EAC5B,UAAU;AAAA,EACV,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -171,7 +171,7 @@ void main() {
|
|
|
171
171
|
r *= (1. + .05 * sin(3. * a + 2. * t));
|
|
172
172
|
float f = abs(cos(a * 3.));
|
|
173
173
|
edge = smoothstep(f, f + .7, r);
|
|
174
|
-
edge
|
|
174
|
+
edge *= edge;
|
|
175
175
|
|
|
176
176
|
uv *= .8;
|
|
177
177
|
cycleWidth *= 1.6;
|
|
@@ -220,7 +220,7 @@ void main() {
|
|
|
220
220
|
} else {
|
|
221
221
|
opacity = 1. - smoothstep(.9 - 2. * fwidth(edge), .9, edge);
|
|
222
222
|
if (u_shape < 2.) {
|
|
223
|
-
edge = 1.2 *
|
|
223
|
+
edge = 1.2 * edge;
|
|
224
224
|
} else if (u_shape < 5.) {
|
|
225
225
|
edge = 1.8 * pow(edge, 1.5);
|
|
226
226
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/shaders/liquid-metal.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, rotation2, simplexNoise, colorBandingFix } from '../shader-utils.js';\n\n/**\n *\n * Fluid motion imitation applied over user image\n * (animated stripe pattern getting distorted with shape edges)\n *\n * Uniforms:\n * - u_colorBack, u_colorTint (RGBA)\n * - u_repetition: density of pattern stripes\n * - u_softness: blur between stripes\n * - u_shiftRed & u_shiftBlue: color dispersion between the stripes\n * - u_distortion: pattern distortion on the whole canvas\n * - u_contour: distortion power over the shape edges\n * - u_shape (float used as integer):\n * ---- 0: canvas-screen rectangle, needs u_worldWidth = u_worldHeight = 0 to be responsive (see vertex shader)\n * ---- 1: static circle\n * ---- 2: animated flower-like polar shape\n * ---- 3: animated metaballs\n *\n */\n\n// language=GLSL\nexport const liquidMetalFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform sampler2D u_image;\nuniform float u_imageAspectRatio;\n\nuniform vec2 u_resolution;\nuniform float u_time;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colorTint;\n\nuniform float u_softness;\nuniform float u_repetition;\nuniform float u_shiftRed;\nuniform float u_shiftBlue;\nuniform float u_distortion;\nuniform float u_contour;\nuniform float u_angle;\n\nuniform float u_shape;\nuniform bool u_isImage;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n${rotation2}\n${simplexNoise}\n\nfloat getColorChanges(float c1, float c2, float stripe_p, vec3 w, float blur, float bump, float tint) {\n\n float ch = mix(c2, c1, smoothstep(.0, 2. * blur, stripe_p));\n\n float border = w[0];\n ch = mix(ch, c2, smoothstep(border, border + 2. * blur, stripe_p));\n\n if (u_isImage == true) {\n bump = smoothstep(.2, .8, bump);\n }\n border = w[0] + .4 * (1. - bump) * w[1];\n ch = mix(ch, c1, smoothstep(border, border + 2. * blur, stripe_p));\n\n border = w[0] + .5 * (1. - bump) * w[1];\n ch = mix(ch, c2, smoothstep(border, border + 2. * blur, stripe_p));\n\n border = w[0] + w[1];\n ch = mix(ch, c1, smoothstep(border, border + 2. * blur, stripe_p));\n\n float gradient_t = (stripe_p - w[0] - w[1]) / w[2];\n float gradient = mix(c1, c2, smoothstep(0., 1., gradient_t));\n ch = mix(ch, gradient, smoothstep(border, border + .5 * blur, stripe_p));\n\n // Tint color is applied with color burn blending\n ch = mix(ch, 1. - min(1., (1. - ch) / max(tint, 0.0001)), u_colorTint.a);\n return ch;\n}\n\nfloat getImgFrame(vec2 uv, float th) {\n float frame = 1.;\n frame *= smoothstep(0., th, uv.y);\n frame *= 1.0 - smoothstep(1. - th, 1., uv.y);\n frame *= smoothstep(0., th, uv.x);\n frame *= 1.0 - smoothstep(1. - th, 1., uv.x);\n return frame;\n}\n\nfloat blurEdge3x3(sampler2D tex, vec2 uv, vec2 dudx, vec2 dudy, float radius, float centerSample) {\n vec2 texel = 1.0 / vec2(textureSize(tex, 0));\n vec2 r = radius * texel;\n\n float w1 = 1.0, w2 = 2.0, w4 = 4.0;\n float norm = 16.0;\n float sum = w4 * centerSample;\n\n sum += w2 * textureGrad(tex, uv + vec2(0.0, -r.y), dudx, dudy).r;\n sum += w2 * textureGrad(tex, uv + vec2(0.0, r.y), dudx, dudy).r;\n sum += w2 * textureGrad(tex, uv + vec2(-r.x, 0.0), dudx, dudy).r;\n sum += w2 * textureGrad(tex, uv + vec2(r.x, 0.0), dudx, dudy).r;\n\n sum += w1 * textureGrad(tex, uv + vec2(-r.x, -r.y), dudx, dudy).r;\n sum += w1 * textureGrad(tex, uv + vec2(r.x, -r.y), dudx, dudy).r;\n sum += w1 * textureGrad(tex, uv + vec2(-r.x, r.y), dudx, dudy).r;\n sum += w1 * textureGrad(tex, uv + vec2(r.x, r.y), dudx, dudy).r;\n\n return sum / norm;\n}\n\nfloat lst(float edge0, float edge1, float x) {\n return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);\n}\n\nvoid main() {\n\n const float firstFrameOffset = 2.8;\n float t = .3 * (u_time + firstFrameOffset);\n\n vec2 uv = v_imageUV;\n vec2 dudx = dFdx(v_imageUV);\n vec2 dudy = dFdy(v_imageUV);\n vec4 img = textureGrad(u_image, uv, dudx, dudy);\n\n if (u_isImage == false) {\n uv = v_objectUV + .5;\n uv.y = 1. - uv.y;\n }\n\n float cycleWidth = u_repetition;\n float edge = 0.;\n float contOffset = 1.;\n\n vec2 rotatedUV = uv - vec2(.5);\n float angle = (-u_angle + 70.) * PI / 180.;\n float cosA = cos(angle);\n float sinA = sin(angle);\n rotatedUV = vec2(\n rotatedUV.x * cosA - rotatedUV.y * sinA,\n rotatedUV.x * sinA + rotatedUV.y * cosA\n ) + vec2(.5);\n\n if (u_isImage == true) {\n float edgeRaw = img.r;\n edge = blurEdge3x3(u_image, uv, dudx, dudy, 6., edgeRaw);\n edge = pow(edge, 1.6);\n edge *= mix(0.0, 1.0, smoothstep(0.0, 0.4, u_contour));\n } else {\n if (u_shape < 1.) {\n // full-fill on canvas\n vec2 borderUV = v_responsiveUV + .5;\n float ratio = v_responsiveBoxGivenSize.x / v_responsiveBoxGivenSize.y;\n vec2 mask = min(borderUV, 1. - borderUV);\n vec2 pixel_thickness = 250. / v_responsiveBoxGivenSize;\n float maskX = smoothstep(0.0, pixel_thickness.x, mask.x);\n float maskY = smoothstep(0.0, pixel_thickness.y, mask.y);\n maskX = pow(maskX, .25);\n maskY = pow(maskY, .25);\n edge = clamp(1. - maskX * maskY, 0., 1.);\n\n uv = v_responsiveUV;\n if (ratio > 1.) {\n uv.y /= ratio;\n } else {\n uv.x *= ratio;\n }\n uv += .5;\n uv.y = 1. - uv.y;\n\n cycleWidth *= 2.;\n contOffset = 1.5;\n\n } else if (u_shape < 2.) {\n // circle\n vec2 shapeUV = uv - .5;\n shapeUV *= .67;\n edge = pow(clamp(3. * length(shapeUV), 0., 1.), 18.);\n } else if (u_shape < 3.) {\n // daisy\n vec2 shapeUV = uv - .5;\n shapeUV *= 1.68;\n\n float r = length(shapeUV) * 2.;\n float a = atan(shapeUV.y, shapeUV.x) + .2;\n r *= (1. + .05 * sin(3. * a + 2. * t));\n float f = abs(cos(a * 3.));\n edge = smoothstep(f, f + .7, r);\n edge = pow(edge, 2.);\n\n uv *= .8;\n cycleWidth *= 1.6;\n\n } else if (u_shape < 4.) {\n // diamond\n vec2 shapeUV = uv - .5;\n shapeUV = rotate(shapeUV, .25 * PI);\n shapeUV *= 1.42;\n shapeUV += .5;\n vec2 mask = min(shapeUV, 1. - shapeUV);\n vec2 pixel_thickness = vec2(.15);\n float maskX = smoothstep(0.0, pixel_thickness.x, mask.x);\n float maskY = smoothstep(0.0, pixel_thickness.y, mask.y);\n maskX = pow(maskX, .25);\n maskY = pow(maskY, .25);\n edge = clamp(1. - maskX * maskY, 0., 1.);\n } else if (u_shape < 5.) {\n // metaballs\n vec2 shapeUV = uv - .5;\n shapeUV *= 1.3;\n edge = 0.;\n for (int i = 0; i < 5; i++) {\n float fi = float(i);\n float speed = 1.5 + 2./3. * sin(fi * 12.345);\n float angle = -fi * 1.5;\n vec2 dir1 = vec2(cos(angle), sin(angle));\n vec2 dir2 = vec2(cos(angle + 1.57), sin(angle + 1.));\n vec2 traj = .4 * (dir1 * sin(t * speed + fi * 1.23) + dir2 * cos(t * (speed * 0.7) + fi * 2.17));\n float d = length(shapeUV + traj);\n edge += pow(1.0 - clamp(d, 0.0, 1.0), 4.0);\n }\n edge = 1. - smoothstep(.65, .9, edge);\n edge = pow(edge, 4.);\n }\n\n edge = mix(smoothstep(.9 - 2. * fwidth(edge), .9, edge), edge, smoothstep(0.0, 0.4, u_contour));\n\n }\n\n float opacity = 0.;\n if (u_isImage == true) {\n opacity = img.g;\n float frame = getImgFrame(v_imageUV, 0.);\n opacity *= frame;\n } else {\n opacity = 1. - smoothstep(.9 - 2. * fwidth(edge), .9, edge);\n if (u_shape < 2.) {\n edge = 1.2 * pow(edge, 1.);\n } else if (u_shape < 5.) {\n edge = 1.8 * pow(edge, 1.5);\n }\n }\n\n float diagBLtoTR = rotatedUV.x - rotatedUV.y;\n float diagTLtoBR = rotatedUV.x + rotatedUV.y;\n\n vec3 color = vec3(0.);\n vec3 color1 = vec3(.98, 0.98, 1.);\n vec3 color2 = vec3(.1, .1, .1 + .1 * smoothstep(.7, 1.3, diagTLtoBR));\n\n vec2 grad_uv = uv - .5;\n\n float dist = length(grad_uv + vec2(0., .2 * diagBLtoTR));\n grad_uv = rotate(grad_uv, (.25 - .2 * diagBLtoTR) * PI);\n float direction = grad_uv.x;\n\n float bump = pow(1.8 * dist, 1.2);\n bump = 1. - bump;\n bump *= pow(uv.y, .3);\n\n\n float thin_strip_1_ratio = .12 / cycleWidth * (1. - .4 * bump);\n float thin_strip_2_ratio = .07 / cycleWidth * (1. + .4 * bump);\n float wide_strip_ratio = (1. - thin_strip_1_ratio - thin_strip_2_ratio);\n\n float thin_strip_1_width = cycleWidth * thin_strip_1_ratio;\n float thin_strip_2_width = cycleWidth * thin_strip_2_ratio;\n\n float noise = snoise(uv - t);\n\n edge += (1. - edge) * u_distortion * noise;\n\n direction += diagBLtoTR;\n float contour = 0.;\n direction -= 2. * noise * diagBLtoTR * (smoothstep(0., 1., edge) * (1.0 - smoothstep(0., 1., edge)));\n direction *= mix(1., 1. - edge, smoothstep(.5, 1., u_contour));\n direction -= 1.7 * edge * smoothstep(.5, 1., u_contour);\n direction += .2 * pow(u_contour, 4.) * (1.0 - smoothstep(0., 1., edge));\n\n bump *= clamp(pow(uv.y, .1), .3, 1.);\n direction *= (.1 + (1.1 - edge) * bump);\n\n direction *= (.4 + .6 * (1.0 - smoothstep(.5, 1., edge)));\n direction += .18 * (smoothstep(.1, .2, uv.y) * (1.0 - smoothstep(.2, .4, uv.y)));\n direction += .03 * (smoothstep(.1, .2, 1. - uv.y) * (1.0 - smoothstep(.2, .4, 1. - uv.y)));\n\n direction *= (.5 + .5 * pow(uv.y, 2.));\n direction *= cycleWidth;\n direction -= t;\n\n\n float colorDispersion = (1. - bump);\n colorDispersion = clamp(colorDispersion, 0., 1.);\n float dispersionRed = colorDispersion;\n dispersionRed += .03 * bump * noise;\n dispersionRed += 5. * (smoothstep(-.1, .2, uv.y) * (1.0 - smoothstep(.1, .5, uv.y))) * (smoothstep(.4, .6, bump) * (1.0 - smoothstep(.4, 1., bump)));\n dispersionRed -= diagBLtoTR;\n\n float dispersionBlue = colorDispersion;\n dispersionBlue *= 1.3;\n dispersionBlue += (smoothstep(0., .4, uv.y) * (1.0 - smoothstep(.1, .8, uv.y))) * (smoothstep(.4, .6, bump) * (1.0 - smoothstep(.4, .8, bump)));\n dispersionBlue -= .2 * edge;\n\n dispersionRed *= (u_shiftRed / 20.);\n dispersionBlue *= (u_shiftBlue / 20.);\n\n float blur = 0.;\n float rExtraBlur = 0.;\n float gExtraBlur = 0.;\n if (u_isImage == true) {\n float softness = 0.05 * u_softness;\n blur = softness + .5 * smoothstep(1., 10., u_repetition) * smoothstep(.0, 1., edge);\n float smallCanvasT = 1.0 - smoothstep(100., 500., min(u_resolution.x, u_resolution.y));\n blur += smallCanvasT * smoothstep(.0, 1., edge);\n rExtraBlur = softness * (0.05 + .1 * (u_shiftRed / 20.) * bump);\n gExtraBlur = softness * 0.05 / max(0.001, abs(1. - diagBLtoTR));\n } else {\n blur = u_softness / 15. + .3 * contour;\n }\n\n vec3 w = vec3(thin_strip_1_width, thin_strip_2_width, wide_strip_ratio);\n w[1] -= .02 * smoothstep(.0, 1., edge + bump);\n float stripe_r = fract(direction + dispersionRed);\n float r = getColorChanges(color1.r, color2.r, stripe_r, w, blur + fwidth(stripe_r) + rExtraBlur, bump, u_colorTint.r);\n float stripe_g = fract(direction);\n float g = getColorChanges(color1.g, color2.g, stripe_g, w, blur + fwidth(stripe_g) + gExtraBlur, bump, u_colorTint.g);\n float stripe_b = fract(direction - dispersionBlue);\n float b = getColorChanges(color1.b, color2.b, stripe_b, w, blur + fwidth(stripe_b), bump, u_colorTint.b);\n\n color = vec3(r, g, b);\n color *= opacity;\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n color = color + bgColor * (1. - opacity);\n opacity = opacity + u_colorBack.a * (1. - opacity);\n\n ${colorBandingFix}\n\n fragColor = vec4(color, opacity);\n}\n`;\n\n// Configuration for Poisson solver\nexport const POISSON_CONFIG_OPTIMIZED = {\n measurePerformance: false, // Set to true to see performance metrics\n workingSize: 512, // Size to solve Poisson at (will upscale to original size)\n iterations: 40, // SOR converges ~2-20x faster than standard Gauss-Seidel\n};\n\n// Precomputed pixel data for sparse processing\ninterface SparsePixelData {\n interiorPixels: Uint32Array; // Indices of interior pixels\n boundaryPixels: Uint32Array; // Indices of boundary pixels\n pixelCount: number;\n // Neighbor indices for each interior pixel (4 neighbors per pixel)\n // Layout: [east, west, north, south] for each pixel\n neighborIndices: Int32Array;\n}\n\nexport function toProcessedLiquidMetal(file: File | string): Promise<{ imageData: ImageData; pngBlob: Blob }> {\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n const isBlob = typeof file === 'string' && file.startsWith('blob:');\n\n return new Promise((resolve, reject) => {\n if (!file || !ctx) {\n reject(new Error('Invalid file or canvas context'));\n return;\n }\n\n const blobContentTypePromise = isBlob && fetch(file).then((res) => res.headers.get('Content-Type'));\n const img = new Image();\n img.crossOrigin = 'anonymous';\n const totalStartTime = performance.now();\n\n img.onload = async () => {\n // Force SVG to load at a high fidelity size if it's an SVG\n let isSVG;\n\n const blobContentType = await blobContentTypePromise;\n\n if (blobContentType) {\n isSVG = blobContentType === 'image/svg+xml';\n } else if (typeof file === 'string') {\n isSVG = file.endsWith('.svg') || file.startsWith('data:image/svg+xml');\n } else {\n isSVG = file.type === 'image/svg+xml';\n }\n\n let originalWidth = img.width || img.naturalWidth;\n let originalHeight = img.height || img.naturalHeight;\n\n if (isSVG) {\n // Scale SVG to max dimension while preserving aspect ratio\n const svgMaxSize = 4096;\n const aspectRatio = originalWidth / originalHeight;\n\n if (originalWidth > originalHeight) {\n originalWidth = svgMaxSize;\n originalHeight = svgMaxSize / aspectRatio;\n } else {\n originalHeight = svgMaxSize;\n originalWidth = svgMaxSize * aspectRatio;\n }\n\n img.width = originalWidth;\n img.height = originalHeight;\n }\n\n // Always scale to working resolution for consistency\n const minDimension = Math.min(originalWidth, originalHeight);\n const targetSize = POISSON_CONFIG_OPTIMIZED.workingSize;\n\n // Calculate scale to fit within workingSize\n const scaleFactor = targetSize / minDimension;\n const width = Math.round(originalWidth * scaleFactor);\n const height = Math.round(originalHeight * scaleFactor);\n\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\n console.log(`[Processing Mode]`);\n console.log(` Original: ${originalWidth}\u00D7${originalHeight}`);\n console.log(` Working: ${width}\u00D7${height} (${(scaleFactor * 100).toFixed(1)}% scale)`);\n if (scaleFactor < 1) {\n console.log(` Speedup: ~${Math.round(1 / (scaleFactor * scaleFactor))}\u00D7`);\n }\n }\n\n canvas.width = originalWidth;\n canvas.height = originalHeight;\n\n // Use a smaller canvas for shape detection and Poisson solving\n const shapeCanvas = document.createElement('canvas');\n shapeCanvas.width = width;\n shapeCanvas.height = height;\n\n const shapeCtx = shapeCanvas.getContext('2d')!;\n shapeCtx.drawImage(img, 0, 0, width, height);\n\n // 1) Build optimized masks using TypedArrays\n const startMask = performance.now();\n\n const shapeImageData = shapeCtx.getImageData(0, 0, width, height);\n const data = shapeImageData.data;\n\n // Use Uint8Array for masks (1 byte per pixel vs 8+ bytes for boolean array)\n const shapeMask = new Uint8Array(width * height);\n const boundaryMask = new Uint8Array(width * height);\n\n // First pass: identify shape pixels\n let shapePixelCount = 0;\n for (let i = 0, idx = 0; i < data.length; i += 4, idx++) {\n const a = data[i + 3];\n const isShape = a === 0 ? 0 : 1;\n shapeMask[idx] = isShape;\n shapePixelCount += isShape;\n }\n\n // 2) Optimized boundary detection using sparse approach\n // Only check shape pixels, not all pixels\n const boundaryIndices: number[] = [];\n const interiorIndices: number[] = [];\n\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const idx = y * width + x;\n if (!shapeMask[idx]) continue;\n\n // Check if pixel is on boundary (optimized: early exit)\n let isBoundary = false;\n\n // Check 4-connected neighbors first (most common case)\n if (x === 0 || x === width - 1 || y === 0 || y === height - 1) {\n isBoundary = true;\n } else {\n // Check all 8 neighbors (including diagonals) for comprehensive boundary detection\n isBoundary =\n !shapeMask[idx - 1] || // left\n !shapeMask[idx + 1] || // right\n !shapeMask[idx - width] || // top\n !shapeMask[idx + width] || // bottom\n !shapeMask[idx - width - 1] || // top-left\n !shapeMask[idx - width + 1] || // top-right\n !shapeMask[idx + width - 1] || // bottom-left\n !shapeMask[idx + width + 1]; // bottom-right\n }\n\n if (isBoundary) {\n boundaryMask[idx] = 1;\n boundaryIndices.push(idx);\n } else {\n interiorIndices.push(idx);\n }\n }\n }\n\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\n console.log(`[Mask Building] Time: ${(performance.now() - startMask).toFixed(2)}ms`);\n console.log(\n ` Shape pixels: ${shapePixelCount} / ${width * height} (${((shapePixelCount / (width * height)) * 100).toFixed(1)}%)`\n );\n console.log(` Interior pixels: ${interiorIndices.length}`);\n console.log(` Boundary pixels: ${boundaryIndices.length}`);\n }\n\n // 3) Precompute sparse data structure for solver\n const sparseData = buildSparseData(\n shapeMask,\n boundaryMask,\n new Uint32Array(interiorIndices),\n new Uint32Array(boundaryIndices),\n width,\n height\n );\n\n // 4) Solve Poisson equation with optimized sparse solver\n const startSolve = performance.now();\n const u = solvePoissonSparse(sparseData, shapeMask, boundaryMask, width, height);\n\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\n console.log(`[Poisson Solve] Time: ${(performance.now() - startSolve).toFixed(2)}ms`);\n }\n\n // 5) Generate output image\n let maxVal = 0;\n let finalImageData: ImageData;\n\n // Only check shape pixels for max value\n for (let i = 0; i < interiorIndices.length; i++) {\n const idx = interiorIndices[i]!;\n if (u[idx]! > maxVal) maxVal = u[idx]!;\n }\n\n // Create gradient image at working resolution\n const tempCanvas = document.createElement('canvas');\n tempCanvas.width = width;\n tempCanvas.height = height;\n const tempCtx = tempCanvas.getContext('2d')!;\n\n const tempImg = tempCtx.createImageData(width, height);\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const idx = y * width + x;\n const px = idx * 4;\n\n if (!shapeMask[idx]) {\n tempImg.data[px] = 255;\n tempImg.data[px + 1] = 255;\n tempImg.data[px + 2] = 255;\n tempImg.data[px + 3] = 0; // Alpha = 0 for background\n } else {\n const poissonRatio = u[idx]! / maxVal;\n const gray = 255 * (1 - poissonRatio);\n tempImg.data[px] = gray;\n tempImg.data[px + 1] = gray;\n tempImg.data[px + 2] = gray;\n tempImg.data[px + 3] = 255; // Alpha = 255 for shape\n }\n }\n }\n tempCtx.putImageData(tempImg, 0, 0);\n\n // Upscale to original resolution with smooth interpolation\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(tempCanvas, 0, 0, width, height, 0, 0, originalWidth, originalHeight);\n\n // Now get the upscaled image data for final output\n const outImg = ctx.getImageData(0, 0, originalWidth, originalHeight);\n\n // Re-apply edges from original resolution with anti-aliasing\n // This ensures edges are pixel-perfect while gradient is smooth\n const originalCanvas = document.createElement('canvas');\n originalCanvas.width = originalWidth;\n originalCanvas.height = originalHeight;\n const originalCtx = originalCanvas.getContext('2d')!;\n // originalCtx.fillStyle = \"white\";\n // originalCtx.fillRect(0, 0, originalWidth, originalHeight);\n originalCtx.drawImage(img, 0, 0, originalWidth, originalHeight);\n const originalData = originalCtx.getImageData(0, 0, originalWidth, originalHeight);\n\n // Process each pixel: Red channel = gradient, Alpha channel = original alpha\n for (let i = 0; i < outImg.data.length; i += 4) {\n const a = originalData.data[i + 3]!;\n // Use only alpha to determine background vs shape\n const upscaledAlpha = outImg.data[i + 3]!;\n if (a === 0) {\n // Background pixel\n outImg.data[i] = 255;\n outImg.data[i + 1] = 0;\n } else {\n // Red channel carries the gradient\n // Check if upscale missed this pixel by looking at alpha channel\n // If upscaled alpha is 0, the low-res version thought this was background\n outImg.data[i] = upscaledAlpha === 0 ? 0 : outImg.data[i]!; // gradient or 0\n outImg.data[i + 1] = a; // original alpha\n }\n\n // Unused channels fixed\n outImg.data[i + 2] = 255;\n outImg.data[i + 3] = 255;\n }\n\n ctx.putImageData(outImg, 0, 0);\n finalImageData = outImg;\n canvas.toBlob((blob) => {\n if (!blob) {\n reject(new Error('Failed to create PNG blob'));\n return;\n }\n\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\n const totalTime = performance.now() - totalStartTime;\n console.log(`[Total Processing Time] ${totalTime.toFixed(2)}ms`);\n if (scaleFactor < 1) {\n const estimatedFullResTime = totalTime * Math.pow((originalWidth * originalHeight) / (width * height), 1.5);\n console.log(`[Estimated time at full resolution] ~${estimatedFullResTime.toFixed(0)}ms`);\n console.log(\n `[Time saved] ~${(estimatedFullResTime - totalTime).toFixed(0)}ms (${Math.round(estimatedFullResTime / totalTime)}\u00D7 faster)`\n );\n }\n }\n\n resolve({\n imageData: finalImageData,\n pngBlob: blob,\n });\n }, 'image/png');\n };\n\n img.onerror = () => reject(new Error('Failed to load image'));\n img.src = typeof file === 'string' ? file : URL.createObjectURL(file);\n });\n}\n\nfunction buildSparseData(\n shapeMask: Uint8Array,\n boundaryMask: Uint8Array,\n interiorPixels: Uint32Array,\n boundaryPixels: Uint32Array,\n width: number,\n height: number\n): SparsePixelData {\n const pixelCount = interiorPixels.length;\n\n // Build neighbor indices for sparse processing\n // For each interior pixel, store indices of its 4 neighbors\n // Use -1 for out-of-bounds or non-shape neighbors\n const neighborIndices = new Int32Array(pixelCount * 4);\n\n for (let i = 0; i < pixelCount; i++) {\n const idx = interiorPixels[i]!;\n const x = idx % width;\n const y = Math.floor(idx / width);\n\n // East neighbor\n neighborIndices[i * 4 + 0] = x < width - 1 && shapeMask[idx + 1] ? idx + 1 : -1;\n // West neighbor\n neighborIndices[i * 4 + 1] = x > 0 && shapeMask[idx - 1] ? idx - 1 : -1;\n // North neighbor\n neighborIndices[i * 4 + 2] = y > 0 && shapeMask[idx - width] ? idx - width : -1;\n // South neighbor\n neighborIndices[i * 4 + 3] = y < height - 1 && shapeMask[idx + width] ? idx + width : -1;\n }\n\n return {\n interiorPixels,\n boundaryPixels,\n pixelCount,\n neighborIndices,\n };\n}\n\nfunction solvePoissonSparse(\n sparseData: SparsePixelData,\n shapeMask: Uint8Array,\n boundaryMask: Uint8Array,\n width: number,\n height: number\n): Float32Array {\n // This controls how smooth the falloff gradient will be and extend into the shape\n const ITERATIONS = POISSON_CONFIG_OPTIMIZED.iterations;\n\n // Keep C constant - only iterations control gradient spread\n const C = 0.01;\n\n const u = new Float32Array(width * height);\n const { interiorPixels, neighborIndices, pixelCount } = sparseData;\n\n // Performance tracking\n const startTime = performance.now();\n\n // Red-Black SOR for better symmetry with fewer iterations\n // omega between 1.8-1.95 typically gives best convergence for Poisson\n const omega = 1.9;\n\n // Pre-classify pixels as red or black for efficient processing\n const redPixels: number[] = [];\n const blackPixels: number[] = [];\n\n for (let i = 0; i < pixelCount; i++) {\n const idx = interiorPixels[i]!;\n const x = idx % width;\n const y = Math.floor(idx / width);\n\n if ((x + y) % 2 === 0) {\n redPixels.push(i);\n } else {\n blackPixels.push(i);\n }\n }\n\n for (let iter = 0; iter < ITERATIONS; iter++) {\n // Red pass: update red pixels\n for (const i of redPixels) {\n const idx = interiorPixels[i]!;\n\n // Get precomputed neighbor indices\n const eastIdx = neighborIndices[i * 4 + 0]!;\n const westIdx = neighborIndices[i * 4 + 1]!;\n const northIdx = neighborIndices[i * 4 + 2]!;\n const southIdx = neighborIndices[i * 4 + 3]!;\n\n // Sum neighbors (use 0 for out-of-bounds)\n let sumN = 0;\n if (eastIdx >= 0) sumN += u[eastIdx]!;\n if (westIdx >= 0) sumN += u[westIdx]!;\n if (northIdx >= 0) sumN += u[northIdx]!;\n if (southIdx >= 0) sumN += u[southIdx]!;\n\n // SOR update: blend new value with old value\n const newValue = (C + sumN) / 4;\n u[idx] = omega * newValue + (1 - omega) * u[idx]!;\n }\n\n // Black pass: update black pixels\n for (const i of blackPixels) {\n const idx = interiorPixels[i]!;\n\n // Get precomputed neighbor indices\n const eastIdx = neighborIndices[i * 4 + 0]!;\n const westIdx = neighborIndices[i * 4 + 1]!;\n const northIdx = neighborIndices[i * 4 + 2]!;\n const southIdx = neighborIndices[i * 4 + 3]!;\n\n // Sum neighbors (use 0 for out-of-bounds)\n let sumN = 0;\n if (eastIdx >= 0) sumN += u[eastIdx]!;\n if (westIdx >= 0) sumN += u[westIdx]!;\n if (northIdx >= 0) sumN += u[northIdx]!;\n if (southIdx >= 0) sumN += u[southIdx]!;\n\n // SOR update: blend new value with old value\n const newValue = (C + sumN) / 4;\n u[idx] = omega * newValue + (1 - omega) * u[idx]!;\n }\n }\n\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\n const elapsed = performance.now() - startTime;\n\n console.log(`[Optimized Poisson Solver (SOR \u03C9=${omega})]`);\n console.log(` Working size: ${width}\u00D7${height}`);\n console.log(` Iterations: ${ITERATIONS}`);\n console.log(` Time: ${elapsed.toFixed(2)}ms`);\n console.log(` Interior pixels processed: ${pixelCount}`);\n console.log(` Speed: ${((ITERATIONS * pixelCount) / (elapsed * 1000)).toFixed(2)} Mpixels/sec`);\n }\n\n return u;\n}\n\nexport interface LiquidMetalUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colorTint: [number, number, number, number];\n u_image: HTMLImageElement | string | undefined;\n u_repetition: number;\n u_shiftRed: number;\n u_shiftBlue: number;\n u_contour: number;\n u_softness: number;\n u_distortion: number;\n u_angle: number;\n u_shape: (typeof LiquidMetalShapes)[LiquidMetalShape];\n u_isImage: boolean;\n}\n\nexport interface LiquidMetalParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colorTint?: string;\n image?: HTMLImageElement | string | undefined;\n repetition?: number;\n shiftRed?: number;\n shiftBlue?: number;\n contour?: number;\n softness?: number;\n distortion?: number;\n angle?: number;\n shape?: LiquidMetalShape;\n}\n\nexport const LiquidMetalShapes = {\n none: 0,\n circle: 1,\n daisy: 2,\n diamond: 3,\n metaballs: 4,\n} as const;\n\nexport type LiquidMetalShape = keyof typeof LiquidMetalShapes;\n"],
|
|
4
|
+
"sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, rotation2, simplexNoise, colorBandingFix } from '../shader-utils.js';\n\n/**\n *\n * Fluid motion imitation applied over user image\n * (animated stripe pattern getting distorted with shape edges)\n *\n * Uniforms:\n * - u_colorBack, u_colorTint (RGBA)\n * - u_repetition: density of pattern stripes\n * - u_softness: blur between stripes\n * - u_shiftRed & u_shiftBlue: color dispersion between the stripes\n * - u_distortion: pattern distortion on the whole canvas\n * - u_contour: distortion power over the shape edges\n * - u_shape (float used as integer):\n * ---- 0: canvas-screen rectangle, needs u_worldWidth = u_worldHeight = 0 to be responsive (see vertex shader)\n * ---- 1: static circle\n * ---- 2: animated flower-like polar shape\n * ---- 3: animated metaballs\n *\n */\n\n// language=GLSL\nexport const liquidMetalFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform sampler2D u_image;\nuniform float u_imageAspectRatio;\n\nuniform vec2 u_resolution;\nuniform float u_time;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colorTint;\n\nuniform float u_softness;\nuniform float u_repetition;\nuniform float u_shiftRed;\nuniform float u_shiftBlue;\nuniform float u_distortion;\nuniform float u_contour;\nuniform float u_angle;\n\nuniform float u_shape;\nuniform bool u_isImage;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n${rotation2}\n${simplexNoise}\n\nfloat getColorChanges(float c1, float c2, float stripe_p, vec3 w, float blur, float bump, float tint) {\n\n float ch = mix(c2, c1, smoothstep(.0, 2. * blur, stripe_p));\n\n float border = w[0];\n ch = mix(ch, c2, smoothstep(border, border + 2. * blur, stripe_p));\n\n if (u_isImage == true) {\n bump = smoothstep(.2, .8, bump);\n }\n border = w[0] + .4 * (1. - bump) * w[1];\n ch = mix(ch, c1, smoothstep(border, border + 2. * blur, stripe_p));\n\n border = w[0] + .5 * (1. - bump) * w[1];\n ch = mix(ch, c2, smoothstep(border, border + 2. * blur, stripe_p));\n\n border = w[0] + w[1];\n ch = mix(ch, c1, smoothstep(border, border + 2. * blur, stripe_p));\n\n float gradient_t = (stripe_p - w[0] - w[1]) / w[2];\n float gradient = mix(c1, c2, smoothstep(0., 1., gradient_t));\n ch = mix(ch, gradient, smoothstep(border, border + .5 * blur, stripe_p));\n\n // Tint color is applied with color burn blending\n ch = mix(ch, 1. - min(1., (1. - ch) / max(tint, 0.0001)), u_colorTint.a);\n return ch;\n}\n\nfloat getImgFrame(vec2 uv, float th) {\n float frame = 1.;\n frame *= smoothstep(0., th, uv.y);\n frame *= 1.0 - smoothstep(1. - th, 1., uv.y);\n frame *= smoothstep(0., th, uv.x);\n frame *= 1.0 - smoothstep(1. - th, 1., uv.x);\n return frame;\n}\n\nfloat blurEdge3x3(sampler2D tex, vec2 uv, vec2 dudx, vec2 dudy, float radius, float centerSample) {\n vec2 texel = 1.0 / vec2(textureSize(tex, 0));\n vec2 r = radius * texel;\n\n float w1 = 1.0, w2 = 2.0, w4 = 4.0;\n float norm = 16.0;\n float sum = w4 * centerSample;\n\n sum += w2 * textureGrad(tex, uv + vec2(0.0, -r.y), dudx, dudy).r;\n sum += w2 * textureGrad(tex, uv + vec2(0.0, r.y), dudx, dudy).r;\n sum += w2 * textureGrad(tex, uv + vec2(-r.x, 0.0), dudx, dudy).r;\n sum += w2 * textureGrad(tex, uv + vec2(r.x, 0.0), dudx, dudy).r;\n\n sum += w1 * textureGrad(tex, uv + vec2(-r.x, -r.y), dudx, dudy).r;\n sum += w1 * textureGrad(tex, uv + vec2(r.x, -r.y), dudx, dudy).r;\n sum += w1 * textureGrad(tex, uv + vec2(-r.x, r.y), dudx, dudy).r;\n sum += w1 * textureGrad(tex, uv + vec2(r.x, r.y), dudx, dudy).r;\n\n return sum / norm;\n}\n\nfloat lst(float edge0, float edge1, float x) {\n return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);\n}\n\nvoid main() {\n\n const float firstFrameOffset = 2.8;\n float t = .3 * (u_time + firstFrameOffset);\n\n vec2 uv = v_imageUV;\n vec2 dudx = dFdx(v_imageUV);\n vec2 dudy = dFdy(v_imageUV);\n vec4 img = textureGrad(u_image, uv, dudx, dudy);\n\n if (u_isImage == false) {\n uv = v_objectUV + .5;\n uv.y = 1. - uv.y;\n }\n\n float cycleWidth = u_repetition;\n float edge = 0.;\n float contOffset = 1.;\n\n vec2 rotatedUV = uv - vec2(.5);\n float angle = (-u_angle + 70.) * PI / 180.;\n float cosA = cos(angle);\n float sinA = sin(angle);\n rotatedUV = vec2(\n rotatedUV.x * cosA - rotatedUV.y * sinA,\n rotatedUV.x * sinA + rotatedUV.y * cosA\n ) + vec2(.5);\n\n if (u_isImage == true) {\n float edgeRaw = img.r;\n edge = blurEdge3x3(u_image, uv, dudx, dudy, 6., edgeRaw);\n edge = pow(edge, 1.6);\n edge *= mix(0.0, 1.0, smoothstep(0.0, 0.4, u_contour));\n } else {\n if (u_shape < 1.) {\n // full-fill on canvas\n vec2 borderUV = v_responsiveUV + .5;\n float ratio = v_responsiveBoxGivenSize.x / v_responsiveBoxGivenSize.y;\n vec2 mask = min(borderUV, 1. - borderUV);\n vec2 pixel_thickness = 250. / v_responsiveBoxGivenSize;\n float maskX = smoothstep(0.0, pixel_thickness.x, mask.x);\n float maskY = smoothstep(0.0, pixel_thickness.y, mask.y);\n maskX = pow(maskX, .25);\n maskY = pow(maskY, .25);\n edge = clamp(1. - maskX * maskY, 0., 1.);\n\n uv = v_responsiveUV;\n if (ratio > 1.) {\n uv.y /= ratio;\n } else {\n uv.x *= ratio;\n }\n uv += .5;\n uv.y = 1. - uv.y;\n\n cycleWidth *= 2.;\n contOffset = 1.5;\n\n } else if (u_shape < 2.) {\n // circle\n vec2 shapeUV = uv - .5;\n shapeUV *= .67;\n edge = pow(clamp(3. * length(shapeUV), 0., 1.), 18.);\n } else if (u_shape < 3.) {\n // daisy\n vec2 shapeUV = uv - .5;\n shapeUV *= 1.68;\n\n float r = length(shapeUV) * 2.;\n float a = atan(shapeUV.y, shapeUV.x) + .2;\n r *= (1. + .05 * sin(3. * a + 2. * t));\n float f = abs(cos(a * 3.));\n edge = smoothstep(f, f + .7, r);\n edge *= edge;\n\n uv *= .8;\n cycleWidth *= 1.6;\n\n } else if (u_shape < 4.) {\n // diamond\n vec2 shapeUV = uv - .5;\n shapeUV = rotate(shapeUV, .25 * PI);\n shapeUV *= 1.42;\n shapeUV += .5;\n vec2 mask = min(shapeUV, 1. - shapeUV);\n vec2 pixel_thickness = vec2(.15);\n float maskX = smoothstep(0.0, pixel_thickness.x, mask.x);\n float maskY = smoothstep(0.0, pixel_thickness.y, mask.y);\n maskX = pow(maskX, .25);\n maskY = pow(maskY, .25);\n edge = clamp(1. - maskX * maskY, 0., 1.);\n } else if (u_shape < 5.) {\n // metaballs\n vec2 shapeUV = uv - .5;\n shapeUV *= 1.3;\n edge = 0.;\n for (int i = 0; i < 5; i++) {\n float fi = float(i);\n float speed = 1.5 + 2./3. * sin(fi * 12.345);\n float angle = -fi * 1.5;\n vec2 dir1 = vec2(cos(angle), sin(angle));\n vec2 dir2 = vec2(cos(angle + 1.57), sin(angle + 1.));\n vec2 traj = .4 * (dir1 * sin(t * speed + fi * 1.23) + dir2 * cos(t * (speed * 0.7) + fi * 2.17));\n float d = length(shapeUV + traj);\n edge += pow(1.0 - clamp(d, 0.0, 1.0), 4.0);\n }\n edge = 1. - smoothstep(.65, .9, edge);\n edge = pow(edge, 4.);\n }\n\n edge = mix(smoothstep(.9 - 2. * fwidth(edge), .9, edge), edge, smoothstep(0.0, 0.4, u_contour));\n\n }\n\n float opacity = 0.;\n if (u_isImage == true) {\n opacity = img.g;\n float frame = getImgFrame(v_imageUV, 0.);\n opacity *= frame;\n } else {\n opacity = 1. - smoothstep(.9 - 2. * fwidth(edge), .9, edge);\n if (u_shape < 2.) {\n edge = 1.2 * edge;\n } else if (u_shape < 5.) {\n edge = 1.8 * pow(edge, 1.5);\n }\n }\n\n float diagBLtoTR = rotatedUV.x - rotatedUV.y;\n float diagTLtoBR = rotatedUV.x + rotatedUV.y;\n\n vec3 color = vec3(0.);\n vec3 color1 = vec3(.98, 0.98, 1.);\n vec3 color2 = vec3(.1, .1, .1 + .1 * smoothstep(.7, 1.3, diagTLtoBR));\n\n vec2 grad_uv = uv - .5;\n\n float dist = length(grad_uv + vec2(0., .2 * diagBLtoTR));\n grad_uv = rotate(grad_uv, (.25 - .2 * diagBLtoTR) * PI);\n float direction = grad_uv.x;\n\n float bump = pow(1.8 * dist, 1.2);\n bump = 1. - bump;\n bump *= pow(uv.y, .3);\n\n\n float thin_strip_1_ratio = .12 / cycleWidth * (1. - .4 * bump);\n float thin_strip_2_ratio = .07 / cycleWidth * (1. + .4 * bump);\n float wide_strip_ratio = (1. - thin_strip_1_ratio - thin_strip_2_ratio);\n\n float thin_strip_1_width = cycleWidth * thin_strip_1_ratio;\n float thin_strip_2_width = cycleWidth * thin_strip_2_ratio;\n\n float noise = snoise(uv - t);\n\n edge += (1. - edge) * u_distortion * noise;\n\n direction += diagBLtoTR;\n float contour = 0.;\n direction -= 2. * noise * diagBLtoTR * (smoothstep(0., 1., edge) * (1.0 - smoothstep(0., 1., edge)));\n direction *= mix(1., 1. - edge, smoothstep(.5, 1., u_contour));\n direction -= 1.7 * edge * smoothstep(.5, 1., u_contour);\n direction += .2 * pow(u_contour, 4.) * (1.0 - smoothstep(0., 1., edge));\n\n bump *= clamp(pow(uv.y, .1), .3, 1.);\n direction *= (.1 + (1.1 - edge) * bump);\n\n direction *= (.4 + .6 * (1.0 - smoothstep(.5, 1., edge)));\n direction += .18 * (smoothstep(.1, .2, uv.y) * (1.0 - smoothstep(.2, .4, uv.y)));\n direction += .03 * (smoothstep(.1, .2, 1. - uv.y) * (1.0 - smoothstep(.2, .4, 1. - uv.y)));\n\n direction *= (.5 + .5 * pow(uv.y, 2.));\n direction *= cycleWidth;\n direction -= t;\n\n\n float colorDispersion = (1. - bump);\n colorDispersion = clamp(colorDispersion, 0., 1.);\n float dispersionRed = colorDispersion;\n dispersionRed += .03 * bump * noise;\n dispersionRed += 5. * (smoothstep(-.1, .2, uv.y) * (1.0 - smoothstep(.1, .5, uv.y))) * (smoothstep(.4, .6, bump) * (1.0 - smoothstep(.4, 1., bump)));\n dispersionRed -= diagBLtoTR;\n\n float dispersionBlue = colorDispersion;\n dispersionBlue *= 1.3;\n dispersionBlue += (smoothstep(0., .4, uv.y) * (1.0 - smoothstep(.1, .8, uv.y))) * (smoothstep(.4, .6, bump) * (1.0 - smoothstep(.4, .8, bump)));\n dispersionBlue -= .2 * edge;\n\n dispersionRed *= (u_shiftRed / 20.);\n dispersionBlue *= (u_shiftBlue / 20.);\n\n float blur = 0.;\n float rExtraBlur = 0.;\n float gExtraBlur = 0.;\n if (u_isImage == true) {\n float softness = 0.05 * u_softness;\n blur = softness + .5 * smoothstep(1., 10., u_repetition) * smoothstep(.0, 1., edge);\n float smallCanvasT = 1.0 - smoothstep(100., 500., min(u_resolution.x, u_resolution.y));\n blur += smallCanvasT * smoothstep(.0, 1., edge);\n rExtraBlur = softness * (0.05 + .1 * (u_shiftRed / 20.) * bump);\n gExtraBlur = softness * 0.05 / max(0.001, abs(1. - diagBLtoTR));\n } else {\n blur = u_softness / 15. + .3 * contour;\n }\n\n vec3 w = vec3(thin_strip_1_width, thin_strip_2_width, wide_strip_ratio);\n w[1] -= .02 * smoothstep(.0, 1., edge + bump);\n float stripe_r = fract(direction + dispersionRed);\n float r = getColorChanges(color1.r, color2.r, stripe_r, w, blur + fwidth(stripe_r) + rExtraBlur, bump, u_colorTint.r);\n float stripe_g = fract(direction);\n float g = getColorChanges(color1.g, color2.g, stripe_g, w, blur + fwidth(stripe_g) + gExtraBlur, bump, u_colorTint.g);\n float stripe_b = fract(direction - dispersionBlue);\n float b = getColorChanges(color1.b, color2.b, stripe_b, w, blur + fwidth(stripe_b), bump, u_colorTint.b);\n\n color = vec3(r, g, b);\n color *= opacity;\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n color = color + bgColor * (1. - opacity);\n opacity = opacity + u_colorBack.a * (1. - opacity);\n\n ${colorBandingFix}\n\n fragColor = vec4(color, opacity);\n}\n`;\n\n// Configuration for Poisson solver\nexport const POISSON_CONFIG_OPTIMIZED = {\n measurePerformance: false, // Set to true to see performance metrics\n workingSize: 512, // Size to solve Poisson at (will upscale to original size)\n iterations: 40, // SOR converges ~2-20x faster than standard Gauss-Seidel\n};\n\n// Precomputed pixel data for sparse processing\ninterface SparsePixelData {\n interiorPixels: Uint32Array; // Indices of interior pixels\n boundaryPixels: Uint32Array; // Indices of boundary pixels\n pixelCount: number;\n // Neighbor indices for each interior pixel (4 neighbors per pixel)\n // Layout: [east, west, north, south] for each pixel\n neighborIndices: Int32Array;\n}\n\nexport function toProcessedLiquidMetal(file: File | string): Promise<{ imageData: ImageData; pngBlob: Blob }> {\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n const isBlob = typeof file === 'string' && file.startsWith('blob:');\n\n return new Promise((resolve, reject) => {\n if (!file || !ctx) {\n reject(new Error('Invalid file or canvas context'));\n return;\n }\n\n const blobContentTypePromise = isBlob && fetch(file).then((res) => res.headers.get('Content-Type'));\n const img = new Image();\n img.crossOrigin = 'anonymous';\n const totalStartTime = performance.now();\n\n img.onload = async () => {\n // Force SVG to load at a high fidelity size if it's an SVG\n let isSVG;\n\n const blobContentType = await blobContentTypePromise;\n\n if (blobContentType) {\n isSVG = blobContentType === 'image/svg+xml';\n } else if (typeof file === 'string') {\n isSVG = file.endsWith('.svg') || file.startsWith('data:image/svg+xml');\n } else {\n isSVG = file.type === 'image/svg+xml';\n }\n\n let originalWidth = img.width || img.naturalWidth;\n let originalHeight = img.height || img.naturalHeight;\n\n if (isSVG) {\n // Scale SVG to max dimension while preserving aspect ratio\n const svgMaxSize = 4096;\n const aspectRatio = originalWidth / originalHeight;\n\n if (originalWidth > originalHeight) {\n originalWidth = svgMaxSize;\n originalHeight = svgMaxSize / aspectRatio;\n } else {\n originalHeight = svgMaxSize;\n originalWidth = svgMaxSize * aspectRatio;\n }\n\n img.width = originalWidth;\n img.height = originalHeight;\n }\n\n // Always scale to working resolution for consistency\n const minDimension = Math.min(originalWidth, originalHeight);\n const targetSize = POISSON_CONFIG_OPTIMIZED.workingSize;\n\n // Calculate scale to fit within workingSize\n const scaleFactor = targetSize / minDimension;\n const width = Math.round(originalWidth * scaleFactor);\n const height = Math.round(originalHeight * scaleFactor);\n\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\n console.log(`[Processing Mode]`);\n console.log(` Original: ${originalWidth}\u00D7${originalHeight}`);\n console.log(` Working: ${width}\u00D7${height} (${(scaleFactor * 100).toFixed(1)}% scale)`);\n if (scaleFactor < 1) {\n console.log(` Speedup: ~${Math.round(1 / (scaleFactor * scaleFactor))}\u00D7`);\n }\n }\n\n canvas.width = originalWidth;\n canvas.height = originalHeight;\n\n // Use a smaller canvas for shape detection and Poisson solving\n const shapeCanvas = document.createElement('canvas');\n shapeCanvas.width = width;\n shapeCanvas.height = height;\n\n const shapeCtx = shapeCanvas.getContext('2d')!;\n shapeCtx.drawImage(img, 0, 0, width, height);\n\n // 1) Build optimized masks using TypedArrays\n const startMask = performance.now();\n\n const shapeImageData = shapeCtx.getImageData(0, 0, width, height);\n const data = shapeImageData.data;\n\n // Use Uint8Array for masks (1 byte per pixel vs 8+ bytes for boolean array)\n const shapeMask = new Uint8Array(width * height);\n const boundaryMask = new Uint8Array(width * height);\n\n // First pass: identify shape pixels\n let shapePixelCount = 0;\n for (let i = 0, idx = 0; i < data.length; i += 4, idx++) {\n const a = data[i + 3];\n const isShape = a === 0 ? 0 : 1;\n shapeMask[idx] = isShape;\n shapePixelCount += isShape;\n }\n\n // 2) Optimized boundary detection using sparse approach\n // Only check shape pixels, not all pixels\n const boundaryIndices: number[] = [];\n const interiorIndices: number[] = [];\n\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const idx = y * width + x;\n if (!shapeMask[idx]) continue;\n\n // Check if pixel is on boundary (optimized: early exit)\n let isBoundary = false;\n\n // Check 4-connected neighbors first (most common case)\n if (x === 0 || x === width - 1 || y === 0 || y === height - 1) {\n isBoundary = true;\n } else {\n // Check all 8 neighbors (including diagonals) for comprehensive boundary detection\n isBoundary =\n !shapeMask[idx - 1] || // left\n !shapeMask[idx + 1] || // right\n !shapeMask[idx - width] || // top\n !shapeMask[idx + width] || // bottom\n !shapeMask[idx - width - 1] || // top-left\n !shapeMask[idx - width + 1] || // top-right\n !shapeMask[idx + width - 1] || // bottom-left\n !shapeMask[idx + width + 1]; // bottom-right\n }\n\n if (isBoundary) {\n boundaryMask[idx] = 1;\n boundaryIndices.push(idx);\n } else {\n interiorIndices.push(idx);\n }\n }\n }\n\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\n console.log(`[Mask Building] Time: ${(performance.now() - startMask).toFixed(2)}ms`);\n console.log(\n ` Shape pixels: ${shapePixelCount} / ${width * height} (${((shapePixelCount / (width * height)) * 100).toFixed(1)}%)`\n );\n console.log(` Interior pixels: ${interiorIndices.length}`);\n console.log(` Boundary pixels: ${boundaryIndices.length}`);\n }\n\n // 3) Precompute sparse data structure for solver\n const sparseData = buildSparseData(\n shapeMask,\n boundaryMask,\n new Uint32Array(interiorIndices),\n new Uint32Array(boundaryIndices),\n width,\n height\n );\n\n // 4) Solve Poisson equation with optimized sparse solver\n const startSolve = performance.now();\n const u = solvePoissonSparse(sparseData, shapeMask, boundaryMask, width, height);\n\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\n console.log(`[Poisson Solve] Time: ${(performance.now() - startSolve).toFixed(2)}ms`);\n }\n\n // 5) Generate output image\n let maxVal = 0;\n let finalImageData: ImageData;\n\n // Only check shape pixels for max value\n for (let i = 0; i < interiorIndices.length; i++) {\n const idx = interiorIndices[i]!;\n if (u[idx]! > maxVal) maxVal = u[idx]!;\n }\n\n // Create gradient image at working resolution\n const tempCanvas = document.createElement('canvas');\n tempCanvas.width = width;\n tempCanvas.height = height;\n const tempCtx = tempCanvas.getContext('2d')!;\n\n const tempImg = tempCtx.createImageData(width, height);\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const idx = y * width + x;\n const px = idx * 4;\n\n if (!shapeMask[idx]) {\n tempImg.data[px] = 255;\n tempImg.data[px + 1] = 255;\n tempImg.data[px + 2] = 255;\n tempImg.data[px + 3] = 0; // Alpha = 0 for background\n } else {\n const poissonRatio = u[idx]! / maxVal;\n const gray = 255 * (1 - poissonRatio);\n tempImg.data[px] = gray;\n tempImg.data[px + 1] = gray;\n tempImg.data[px + 2] = gray;\n tempImg.data[px + 3] = 255; // Alpha = 255 for shape\n }\n }\n }\n tempCtx.putImageData(tempImg, 0, 0);\n\n // Upscale to original resolution with smooth interpolation\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(tempCanvas, 0, 0, width, height, 0, 0, originalWidth, originalHeight);\n\n // Now get the upscaled image data for final output\n const outImg = ctx.getImageData(0, 0, originalWidth, originalHeight);\n\n // Re-apply edges from original resolution with anti-aliasing\n // This ensures edges are pixel-perfect while gradient is smooth\n const originalCanvas = document.createElement('canvas');\n originalCanvas.width = originalWidth;\n originalCanvas.height = originalHeight;\n const originalCtx = originalCanvas.getContext('2d')!;\n // originalCtx.fillStyle = \"white\";\n // originalCtx.fillRect(0, 0, originalWidth, originalHeight);\n originalCtx.drawImage(img, 0, 0, originalWidth, originalHeight);\n const originalData = originalCtx.getImageData(0, 0, originalWidth, originalHeight);\n\n // Process each pixel: Red channel = gradient, Alpha channel = original alpha\n for (let i = 0; i < outImg.data.length; i += 4) {\n const a = originalData.data[i + 3]!;\n // Use only alpha to determine background vs shape\n const upscaledAlpha = outImg.data[i + 3]!;\n if (a === 0) {\n // Background pixel\n outImg.data[i] = 255;\n outImg.data[i + 1] = 0;\n } else {\n // Red channel carries the gradient\n // Check if upscale missed this pixel by looking at alpha channel\n // If upscaled alpha is 0, the low-res version thought this was background\n outImg.data[i] = upscaledAlpha === 0 ? 0 : outImg.data[i]!; // gradient or 0\n outImg.data[i + 1] = a; // original alpha\n }\n\n // Unused channels fixed\n outImg.data[i + 2] = 255;\n outImg.data[i + 3] = 255;\n }\n\n ctx.putImageData(outImg, 0, 0);\n finalImageData = outImg;\n canvas.toBlob((blob) => {\n if (!blob) {\n reject(new Error('Failed to create PNG blob'));\n return;\n }\n\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\n const totalTime = performance.now() - totalStartTime;\n console.log(`[Total Processing Time] ${totalTime.toFixed(2)}ms`);\n if (scaleFactor < 1) {\n const estimatedFullResTime = totalTime * Math.pow((originalWidth * originalHeight) / (width * height), 1.5);\n console.log(`[Estimated time at full resolution] ~${estimatedFullResTime.toFixed(0)}ms`);\n console.log(\n `[Time saved] ~${(estimatedFullResTime - totalTime).toFixed(0)}ms (${Math.round(estimatedFullResTime / totalTime)}\u00D7 faster)`\n );\n }\n }\n\n resolve({\n imageData: finalImageData,\n pngBlob: blob,\n });\n }, 'image/png');\n };\n\n img.onerror = () => reject(new Error('Failed to load image'));\n img.src = typeof file === 'string' ? file : URL.createObjectURL(file);\n });\n}\n\nfunction buildSparseData(\n shapeMask: Uint8Array,\n boundaryMask: Uint8Array,\n interiorPixels: Uint32Array,\n boundaryPixels: Uint32Array,\n width: number,\n height: number\n): SparsePixelData {\n const pixelCount = interiorPixels.length;\n\n // Build neighbor indices for sparse processing\n // For each interior pixel, store indices of its 4 neighbors\n // Use -1 for out-of-bounds or non-shape neighbors\n const neighborIndices = new Int32Array(pixelCount * 4);\n\n for (let i = 0; i < pixelCount; i++) {\n const idx = interiorPixels[i]!;\n const x = idx % width;\n const y = Math.floor(idx / width);\n\n // East neighbor\n neighborIndices[i * 4 + 0] = x < width - 1 && shapeMask[idx + 1] ? idx + 1 : -1;\n // West neighbor\n neighborIndices[i * 4 + 1] = x > 0 && shapeMask[idx - 1] ? idx - 1 : -1;\n // North neighbor\n neighborIndices[i * 4 + 2] = y > 0 && shapeMask[idx - width] ? idx - width : -1;\n // South neighbor\n neighborIndices[i * 4 + 3] = y < height - 1 && shapeMask[idx + width] ? idx + width : -1;\n }\n\n return {\n interiorPixels,\n boundaryPixels,\n pixelCount,\n neighborIndices,\n };\n}\n\nfunction solvePoissonSparse(\n sparseData: SparsePixelData,\n shapeMask: Uint8Array,\n boundaryMask: Uint8Array,\n width: number,\n height: number\n): Float32Array {\n // This controls how smooth the falloff gradient will be and extend into the shape\n const ITERATIONS = POISSON_CONFIG_OPTIMIZED.iterations;\n\n // Keep C constant - only iterations control gradient spread\n const C = 0.01;\n\n const u = new Float32Array(width * height);\n const { interiorPixels, neighborIndices, pixelCount } = sparseData;\n\n // Performance tracking\n const startTime = performance.now();\n\n // Red-Black SOR for better symmetry with fewer iterations\n // omega between 1.8-1.95 typically gives best convergence for Poisson\n const omega = 1.9;\n\n // Pre-classify pixels as red or black for efficient processing\n const redPixels: number[] = [];\n const blackPixels: number[] = [];\n\n for (let i = 0; i < pixelCount; i++) {\n const idx = interiorPixels[i]!;\n const x = idx % width;\n const y = Math.floor(idx / width);\n\n if ((x + y) % 2 === 0) {\n redPixels.push(i);\n } else {\n blackPixels.push(i);\n }\n }\n\n for (let iter = 0; iter < ITERATIONS; iter++) {\n // Red pass: update red pixels\n for (const i of redPixels) {\n const idx = interiorPixels[i]!;\n\n // Get precomputed neighbor indices\n const eastIdx = neighborIndices[i * 4 + 0]!;\n const westIdx = neighborIndices[i * 4 + 1]!;\n const northIdx = neighborIndices[i * 4 + 2]!;\n const southIdx = neighborIndices[i * 4 + 3]!;\n\n // Sum neighbors (use 0 for out-of-bounds)\n let sumN = 0;\n if (eastIdx >= 0) sumN += u[eastIdx]!;\n if (westIdx >= 0) sumN += u[westIdx]!;\n if (northIdx >= 0) sumN += u[northIdx]!;\n if (southIdx >= 0) sumN += u[southIdx]!;\n\n // SOR update: blend new value with old value\n const newValue = (C + sumN) / 4;\n u[idx] = omega * newValue + (1 - omega) * u[idx]!;\n }\n\n // Black pass: update black pixels\n for (const i of blackPixels) {\n const idx = interiorPixels[i]!;\n\n // Get precomputed neighbor indices\n const eastIdx = neighborIndices[i * 4 + 0]!;\n const westIdx = neighborIndices[i * 4 + 1]!;\n const northIdx = neighborIndices[i * 4 + 2]!;\n const southIdx = neighborIndices[i * 4 + 3]!;\n\n // Sum neighbors (use 0 for out-of-bounds)\n let sumN = 0;\n if (eastIdx >= 0) sumN += u[eastIdx]!;\n if (westIdx >= 0) sumN += u[westIdx]!;\n if (northIdx >= 0) sumN += u[northIdx]!;\n if (southIdx >= 0) sumN += u[southIdx]!;\n\n // SOR update: blend new value with old value\n const newValue = (C + sumN) / 4;\n u[idx] = omega * newValue + (1 - omega) * u[idx]!;\n }\n }\n\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\n const elapsed = performance.now() - startTime;\n\n console.log(`[Optimized Poisson Solver (SOR \u03C9=${omega})]`);\n console.log(` Working size: ${width}\u00D7${height}`);\n console.log(` Iterations: ${ITERATIONS}`);\n console.log(` Time: ${elapsed.toFixed(2)}ms`);\n console.log(` Interior pixels processed: ${pixelCount}`);\n console.log(` Speed: ${((ITERATIONS * pixelCount) / (elapsed * 1000)).toFixed(2)} Mpixels/sec`);\n }\n\n return u;\n}\n\nexport interface LiquidMetalUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colorTint: [number, number, number, number];\n u_image: HTMLImageElement | string | undefined;\n u_repetition: number;\n u_shiftRed: number;\n u_shiftBlue: number;\n u_contour: number;\n u_softness: number;\n u_distortion: number;\n u_angle: number;\n u_shape: (typeof LiquidMetalShapes)[LiquidMetalShape];\n u_isImage: boolean;\n}\n\nexport interface LiquidMetalParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colorTint?: string;\n image?: HTMLImageElement | string | undefined;\n repetition?: number;\n shiftRed?: number;\n shiftBlue?: number;\n contour?: number;\n softness?: number;\n distortion?: number;\n angle?: number;\n shape?: LiquidMetalShape;\n}\n\nexport const LiquidMetalShapes = {\n none: 0,\n circle: 1,\n daisy: 2,\n diamond: 3,\n metaballs: 4,\n} as const;\n\nexport type LiquidMetalShape = keyof typeof LiquidMetalShapes;\n"],
|
|
5
5
|
"mappings": ";;;;;AACA,SAAS,kCAAsF;AAC/F,SAAS,WAAW,WAAW,cAAc,uBAAuB;AAuB7D,MAAM,4BAAoC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuB/C,0BAA0B;AAAA;AAAA;AAAA;AAAA,EAI1B,SAAS;AAAA,EACT,SAAS;AAAA,EACT,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA6RV,eAAe;AAAA;AAAA;AAAA;AAAA;AAOZ,MAAM,2BAA2B;AAAA,EACtC,oBAAoB;AAAA;AAAA,EACpB,aAAa;AAAA;AAAA,EACb,YAAY;AAAA;AACd;AAYO,SAAS,uBAAuB,MAAuE;AAC5G,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,QAAM,MAAM,OAAO,WAAW,IAAI;AAClC,QAAM,SAAS,OAAO,SAAS,YAAY,KAAK,WAAW,OAAO;AAElE,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,CAAC,QAAQ,CAAC,KAAK;AACjB,aAAO,IAAI,MAAM,gCAAgC,CAAC;AAClD;AAAA,IACF;AAEA,UAAM,yBAAyB,UAAU,MAAM,IAAI,EAAE,KAAK,CAAC,QAAQ,IAAI,QAAQ,IAAI,cAAc,CAAC;AAClG,UAAM,MAAM,IAAI,MAAM;AACtB,QAAI,cAAc;AAClB,UAAM,iBAAiB,YAAY,IAAI;AAEvC,QAAI,SAAS,YAAY;AAEvB,UAAI;AAEJ,YAAM,kBAAkB,MAAM;AAE9B,UAAI,iBAAiB;AACnB,gBAAQ,oBAAoB;AAAA,MAC9B,WAAW,OAAO,SAAS,UAAU;AACnC,gBAAQ,KAAK,SAAS,MAAM,KAAK,KAAK,WAAW,oBAAoB;AAAA,MACvE,OAAO;AACL,gBAAQ,KAAK,SAAS;AAAA,MACxB;AAEA,UAAI,gBAAgB,IAAI,SAAS,IAAI;AACrC,UAAI,iBAAiB,IAAI,UAAU,IAAI;AAEvC,UAAI,OAAO;AAET,cAAM,aAAa;AACnB,cAAM,cAAc,gBAAgB;AAEpC,YAAI,gBAAgB,gBAAgB;AAClC,0BAAgB;AAChB,2BAAiB,aAAa;AAAA,QAChC,OAAO;AACL,2BAAiB;AACjB,0BAAgB,aAAa;AAAA,QAC/B;AAEA,YAAI,QAAQ;AACZ,YAAI,SAAS;AAAA,MACf;AAGA,YAAM,eAAe,KAAK,IAAI,eAAe,cAAc;AAC3D,YAAM,aAAa,yBAAyB;AAG5C,YAAM,cAAc,aAAa;AACjC,YAAM,QAAQ,KAAK,MAAM,gBAAgB,WAAW;AACpD,YAAM,SAAS,KAAK,MAAM,iBAAiB,WAAW;AAEtD,UAAI,yBAAyB,oBAAoB;AAC/C,gBAAQ,IAAI,mBAAmB;AAC/B,gBAAQ,IAAI,eAAe,aAAa,OAAI,cAAc,EAAE;AAC5D,gBAAQ,IAAI,cAAc,KAAK,OAAI,MAAM,MAAM,cAAc,KAAK,QAAQ,CAAC,CAAC,UAAU;AACtF,YAAI,cAAc,GAAG;AACnB,kBAAQ,IAAI,eAAe,KAAK,MAAM,KAAK,cAAc,YAAY,CAAC,MAAG;AAAA,QAC3E;AAAA,MACF;AAEA,aAAO,QAAQ;AACf,aAAO,SAAS;AAGhB,YAAM,cAAc,SAAS,cAAc,QAAQ;AACnD,kBAAY,QAAQ;AACpB,kBAAY,SAAS;AAErB,YAAM,WAAW,YAAY,WAAW,IAAI;AAC5C,eAAS,UAAU,KAAK,GAAG,GAAG,OAAO,MAAM;AAG3C,YAAM,YAAY,YAAY,IAAI;AAElC,YAAM,iBAAiB,SAAS,aAAa,GAAG,GAAG,OAAO,MAAM;AAChE,YAAM,OAAO,eAAe;AAG5B,YAAM,YAAY,IAAI,WAAW,QAAQ,MAAM;AAC/C,YAAM,eAAe,IAAI,WAAW,QAAQ,MAAM;AAGlD,UAAI,kBAAkB;AACtB,eAAS,IAAI,GAAG,MAAM,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG,OAAO;AACvD,cAAM,IAAI,KAAK,IAAI,CAAC;AACpB,cAAM,UAAU,MAAM,IAAI,IAAI;AAC9B,kBAAU,GAAG,IAAI;AACjB,2BAAmB;AAAA,MACrB;AAIA,YAAM,kBAA4B,CAAC;AACnC,YAAM,kBAA4B,CAAC;AAEnC,eAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,iBAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC9B,gBAAM,MAAM,IAAI,QAAQ;AACxB,cAAI,CAAC,UAAU,GAAG,EAAG;AAGrB,cAAI,aAAa;AAGjB,cAAI,MAAM,KAAK,MAAM,QAAQ,KAAK,MAAM,KAAK,MAAM,SAAS,GAAG;AAC7D,yBAAa;AAAA,UACf,OAAO;AAEL,yBACE,CAAC,UAAU,MAAM,CAAC;AAAA,YAClB,CAAC,UAAU,MAAM,CAAC;AAAA,YAClB,CAAC,UAAU,MAAM,KAAK;AAAA,YACtB,CAAC,UAAU,MAAM,KAAK;AAAA,YACtB,CAAC,UAAU,MAAM,QAAQ,CAAC;AAAA,YAC1B,CAAC,UAAU,MAAM,QAAQ,CAAC;AAAA,YAC1B,CAAC,UAAU,MAAM,QAAQ,CAAC;AAAA,YAC1B,CAAC,UAAU,MAAM,QAAQ,CAAC;AAAA,UAC9B;AAEA,cAAI,YAAY;AACd,yBAAa,GAAG,IAAI;AACpB,4BAAgB,KAAK,GAAG;AAAA,UAC1B,OAAO;AACL,4BAAgB,KAAK,GAAG;AAAA,UAC1B;AAAA,QACF;AAAA,MACF;AAEA,UAAI,yBAAyB,oBAAoB;AAC/C,gBAAQ,IAAI,0BAA0B,YAAY,IAAI,IAAI,WAAW,QAAQ,CAAC,CAAC,IAAI;AACnF,gBAAQ;AAAA,UACN,mBAAmB,eAAe,MAAM,QAAQ,MAAM,MAAO,mBAAmB,QAAQ,UAAW,KAAK,QAAQ,CAAC,CAAC;AAAA,QACpH;AACA,gBAAQ,IAAI,sBAAsB,gBAAgB,MAAM,EAAE;AAC1D,gBAAQ,IAAI,sBAAsB,gBAAgB,MAAM,EAAE;AAAA,MAC5D;AAGA,YAAM,aAAa;AAAA,QACjB;AAAA,QACA;AAAA,QACA,IAAI,YAAY,eAAe;AAAA,QAC/B,IAAI,YAAY,eAAe;AAAA,QAC/B;AAAA,QACA;AAAA,MACF;AAGA,YAAM,aAAa,YAAY,IAAI;AACnC,YAAM,IAAI,mBAAmB,YAAY,WAAW,cAAc,OAAO,MAAM;AAE/E,UAAI,yBAAyB,oBAAoB;AAC/C,gBAAQ,IAAI,0BAA0B,YAAY,IAAI,IAAI,YAAY,QAAQ,CAAC,CAAC,IAAI;AAAA,MACtF;AAGA,UAAI,SAAS;AACb,UAAI;AAGJ,eAAS,IAAI,GAAG,IAAI,gBAAgB,QAAQ,KAAK;AAC/C,cAAM,MAAM,gBAAgB,CAAC;AAC7B,YAAI,EAAE,GAAG,IAAK,OAAQ,UAAS,EAAE,GAAG;AAAA,MACtC;AAGA,YAAM,aAAa,SAAS,cAAc,QAAQ;AAClD,iBAAW,QAAQ;AACnB,iBAAW,SAAS;AACpB,YAAM,UAAU,WAAW,WAAW,IAAI;AAE1C,YAAM,UAAU,QAAQ,gBAAgB,OAAO,MAAM;AACrD,eAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,iBAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC9B,gBAAM,MAAM,IAAI,QAAQ;AACxB,gBAAM,KAAK,MAAM;AAEjB,cAAI,CAAC,UAAU,GAAG,GAAG;AACnB,oBAAQ,KAAK,EAAE,IAAI;AACnB,oBAAQ,KAAK,KAAK,CAAC,IAAI;AACvB,oBAAQ,KAAK,KAAK,CAAC,IAAI;AACvB,oBAAQ,KAAK,KAAK,CAAC,IAAI;AAAA,UACzB,OAAO;AACL,kBAAM,eAAe,EAAE,GAAG,IAAK;AAC/B,kBAAM,OAAO,OAAO,IAAI;AACxB,oBAAQ,KAAK,EAAE,IAAI;AACnB,oBAAQ,KAAK,KAAK,CAAC,IAAI;AACvB,oBAAQ,KAAK,KAAK,CAAC,IAAI;AACvB,oBAAQ,KAAK,KAAK,CAAC,IAAI;AAAA,UACzB;AAAA,QACF;AAAA,MACF;AACA,cAAQ,aAAa,SAAS,GAAG,CAAC;AAGlC,UAAI,wBAAwB;AAC5B,UAAI,wBAAwB;AAC5B,UAAI,UAAU,YAAY,GAAG,GAAG,OAAO,QAAQ,GAAG,GAAG,eAAe,cAAc;AAGlF,YAAM,SAAS,IAAI,aAAa,GAAG,GAAG,eAAe,cAAc;AAInE,YAAM,iBAAiB,SAAS,cAAc,QAAQ;AACtD,qBAAe,QAAQ;AACvB,qBAAe,SAAS;AACxB,YAAM,cAAc,eAAe,WAAW,IAAI;AAGlD,kBAAY,UAAU,KAAK,GAAG,GAAG,eAAe,cAAc;AAC9D,YAAM,eAAe,YAAY,aAAa,GAAG,GAAG,eAAe,cAAc;AAGjF,eAAS,IAAI,GAAG,IAAI,OAAO,KAAK,QAAQ,KAAK,GAAG;AAC9C,cAAM,IAAI,aAAa,KAAK,IAAI,CAAC;AAEjC,cAAM,gBAAgB,OAAO,KAAK,IAAI,CAAC;AACvC,YAAI,MAAM,GAAG;AAEX,iBAAO,KAAK,CAAC,IAAI;AACjB,iBAAO,KAAK,IAAI,CAAC,IAAI;AAAA,QACvB,OAAO;AAIL,iBAAO,KAAK,CAAC,IAAI,kBAAkB,IAAI,IAAI,OAAO,KAAK,CAAC;AACxD,iBAAO,KAAK,IAAI,CAAC,IAAI;AAAA,QACvB;AAGA,eAAO,KAAK,IAAI,CAAC,IAAI;AACrB,eAAO,KAAK,IAAI,CAAC,IAAI;AAAA,MACvB;AAEA,UAAI,aAAa,QAAQ,GAAG,CAAC;AAC7B,uBAAiB;AACjB,aAAO,OAAO,CAAC,SAAS;AACtB,YAAI,CAAC,MAAM;AACT,iBAAO,IAAI,MAAM,2BAA2B,CAAC;AAC7C;AAAA,QACF;AAEA,YAAI,yBAAyB,oBAAoB;AAC/C,gBAAM,YAAY,YAAY,IAAI,IAAI;AACtC,kBAAQ,IAAI,2BAA2B,UAAU,QAAQ,CAAC,CAAC,IAAI;AAC/D,cAAI,cAAc,GAAG;AACnB,kBAAM,uBAAuB,YAAY,KAAK,IAAK,gBAAgB,kBAAmB,QAAQ,SAAS,GAAG;AAC1G,oBAAQ,IAAI,wCAAwC,qBAAqB,QAAQ,CAAC,CAAC,IAAI;AACvF,oBAAQ;AAAA,cACN,kBAAkB,uBAAuB,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,MAAM,uBAAuB,SAAS,CAAC;AAAA,YACnH;AAAA,UACF;AAAA,QACF;AAEA,gBAAQ;AAAA,UACN,WAAW;AAAA,UACX,SAAS;AAAA,QACX,CAAC;AAAA,MACH,GAAG,WAAW;AAAA,IAChB;AAEA,QAAI,UAAU,MAAM,OAAO,IAAI,MAAM,sBAAsB,CAAC;AAC5D,QAAI,MAAM,OAAO,SAAS,WAAW,OAAO,IAAI,gBAAgB,IAAI;AAAA,EACtE,CAAC;AACH;AAEA,SAAS,gBACP,WACA,cACA,gBACA,gBACA,OACA,QACiB;AACjB,QAAM,aAAa,eAAe;AAKlC,QAAM,kBAAkB,IAAI,WAAW,aAAa,CAAC;AAErD,WAAS,IAAI,GAAG,IAAI,YAAY,KAAK;AACnC,UAAM,MAAM,eAAe,CAAC;AAC5B,UAAM,IAAI,MAAM;AAChB,UAAM,IAAI,KAAK,MAAM,MAAM,KAAK;AAGhC,oBAAgB,IAAI,IAAI,CAAC,IAAI,IAAI,QAAQ,KAAK,UAAU,MAAM,CAAC,IAAI,MAAM,IAAI;AAE7E,oBAAgB,IAAI,IAAI,CAAC,IAAI,IAAI,KAAK,UAAU,MAAM,CAAC,IAAI,MAAM,IAAI;AAErE,oBAAgB,IAAI,IAAI,CAAC,IAAI,IAAI,KAAK,UAAU,MAAM,KAAK,IAAI,MAAM,QAAQ;AAE7E,oBAAgB,IAAI,IAAI,CAAC,IAAI,IAAI,SAAS,KAAK,UAAU,MAAM,KAAK,IAAI,MAAM,QAAQ;AAAA,EACxF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,mBACP,YACA,WACA,cACA,OACA,QACc;AAEd,QAAM,aAAa,yBAAyB;AAG5C,QAAM,IAAI;AAEV,QAAM,IAAI,IAAI,aAAa,QAAQ,MAAM;AACzC,QAAM,EAAE,gBAAgB,iBAAiB,WAAW,IAAI;AAGxD,QAAM,YAAY,YAAY,IAAI;AAIlC,QAAM,QAAQ;AAGd,QAAM,YAAsB,CAAC;AAC7B,QAAM,cAAwB,CAAC;AAE/B,WAAS,IAAI,GAAG,IAAI,YAAY,KAAK;AACnC,UAAM,MAAM,eAAe,CAAC;AAC5B,UAAM,IAAI,MAAM;AAChB,UAAM,IAAI,KAAK,MAAM,MAAM,KAAK;AAEhC,SAAK,IAAI,KAAK,MAAM,GAAG;AACrB,gBAAU,KAAK,CAAC;AAAA,IAClB,OAAO;AACL,kBAAY,KAAK,CAAC;AAAA,IACpB;AAAA,EACF;AAEA,WAAS,OAAO,GAAG,OAAO,YAAY,QAAQ;AAE5C,eAAW,KAAK,WAAW;AACzB,YAAM,MAAM,eAAe,CAAC;AAG5B,YAAM,UAAU,gBAAgB,IAAI,IAAI,CAAC;AACzC,YAAM,UAAU,gBAAgB,IAAI,IAAI,CAAC;AACzC,YAAM,WAAW,gBAAgB,IAAI,IAAI,CAAC;AAC1C,YAAM,WAAW,gBAAgB,IAAI,IAAI,CAAC;AAG1C,UAAI,OAAO;AACX,UAAI,WAAW,EAAG,SAAQ,EAAE,OAAO;AACnC,UAAI,WAAW,EAAG,SAAQ,EAAE,OAAO;AACnC,UAAI,YAAY,EAAG,SAAQ,EAAE,QAAQ;AACrC,UAAI,YAAY,EAAG,SAAQ,EAAE,QAAQ;AAGrC,YAAM,YAAY,IAAI,QAAQ;AAC9B,QAAE,GAAG,IAAI,QAAQ,YAAY,IAAI,SAAS,EAAE,GAAG;AAAA,IACjD;AAGA,eAAW,KAAK,aAAa;AAC3B,YAAM,MAAM,eAAe,CAAC;AAG5B,YAAM,UAAU,gBAAgB,IAAI,IAAI,CAAC;AACzC,YAAM,UAAU,gBAAgB,IAAI,IAAI,CAAC;AACzC,YAAM,WAAW,gBAAgB,IAAI,IAAI,CAAC;AAC1C,YAAM,WAAW,gBAAgB,IAAI,IAAI,CAAC;AAG1C,UAAI,OAAO;AACX,UAAI,WAAW,EAAG,SAAQ,EAAE,OAAO;AACnC,UAAI,WAAW,EAAG,SAAQ,EAAE,OAAO;AACnC,UAAI,YAAY,EAAG,SAAQ,EAAE,QAAQ;AACrC,UAAI,YAAY,EAAG,SAAQ,EAAE,QAAQ;AAGrC,YAAM,YAAY,IAAI,QAAQ;AAC9B,QAAE,GAAG,IAAI,QAAQ,YAAY,IAAI,SAAS,EAAE,GAAG;AAAA,IACjD;AAAA,EACF;AAEA,MAAI,yBAAyB,oBAAoB;AAC/C,UAAM,UAAU,YAAY,IAAI,IAAI;AAEpC,YAAQ,IAAI,yCAAoC,KAAK,IAAI;AACzD,YAAQ,IAAI,mBAAmB,KAAK,OAAI,MAAM,EAAE;AAChD,YAAQ,IAAI,iBAAiB,UAAU,EAAE;AACzC,YAAQ,IAAI,WAAW,QAAQ,QAAQ,CAAC,CAAC,IAAI;AAC7C,YAAQ,IAAI,gCAAgC,UAAU,EAAE;AACxD,YAAQ,IAAI,aAAc,aAAa,cAAe,UAAU,MAAO,QAAQ,CAAC,CAAC,cAAc;AAAA,EACjG;AAEA,SAAO;AACT;AA+BO,MAAM,oBAAoB;AAAA,EAC/B,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,SAAS;AAAA,EACT,WAAW;AACb;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -54,8 +54,8 @@ float noise(vec2 n, vec2 seedOffset) {
|
|
|
54
54
|
|
|
55
55
|
vec2 getPosition(int i, float t) {
|
|
56
56
|
float a = float(i) * .37;
|
|
57
|
-
float b = .6 +
|
|
58
|
-
float c = .8 +
|
|
57
|
+
float b = .6 + fract(float(i) / 3.) * .9;
|
|
58
|
+
float c = .8 + fract(float(i + 1) / 4.);
|
|
59
59
|
|
|
60
60
|
float x = sin(t * b + a);
|
|
61
61
|
float y = cos(t * c + a * 1.5);
|
|
@@ -117,8 +117,8 @@ void main() {
|
|
|
117
117
|
totalWeight += weight;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
color /= totalWeight;
|
|
121
|
-
opacity /= totalWeight;
|
|
120
|
+
color /= max(1e-4, totalWeight);
|
|
121
|
+
opacity /= max(1e-4, totalWeight);
|
|
122
122
|
|
|
123
123
|
float rr = noise(rotate(grainUV, 1.), vec2(3.));
|
|
124
124
|
float gg = noise(rotate(grainUV, 2.) + 10., vec2(-1.));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/shaders/mesh-gradient.ts"],
|
|
4
|
-
"sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport {\n sizingDebugVariablesDeclaration,\n sizingVariablesDeclaration,\n sizingUniformsDeclaration,\n type ShaderSizingParams,\n type ShaderSizingUniforms,\n} from '../shader-sizing.js';\nimport { declarePI, rotation2, proceduralHash21 } from '../shader-utils.js';\n\nexport const meshGradientMeta = {\n maxColorCount: 10,\n} as const;\n\n/**\n * A composition of N color spots (one per color) with 2 types of\n * distortions applied to the coordinate space\n *\n * Uniforms:\n * - u_colors (vec4[]), u_colorsCount (float used as integer)\n * - u_distortion: warp distortion\n * - u_swirl: vortex distortion\n * - u_grainMixer: shape distortion\n * - u_grainOverlay: post-processing blending\n */\n\n// language=GLSL\nexport const meshGradientFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform vec4 u_colors[${meshGradientMeta.maxColorCount}];\nuniform float u_colorsCount;\n\nuniform float u_distortion;\nuniform float u_swirl;\nuniform float u_grainMixer;\nuniform float u_grainOverlay;\n\n${sizingVariablesDeclaration}\n${sizingDebugVariablesDeclaration}\n${sizingUniformsDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n${rotation2}\n${proceduralHash21}\n\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = hash21(i);\n float b = hash21(i + vec2(1.0, 0.0));\n float c = hash21(i + vec2(0.0, 1.0));\n float d = hash21(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\n\nfloat noise(vec2 n, vec2 seedOffset) {\n return valueNoise(n + seedOffset);\n}\n\nvec2 getPosition(int i, float t) {\n float a = float(i) * .37;\n float b = .6 +
|
|
4
|
+
"sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport {\n sizingDebugVariablesDeclaration,\n sizingVariablesDeclaration,\n sizingUniformsDeclaration,\n type ShaderSizingParams,\n type ShaderSizingUniforms,\n} from '../shader-sizing.js';\nimport { declarePI, rotation2, proceduralHash21 } from '../shader-utils.js';\n\nexport const meshGradientMeta = {\n maxColorCount: 10,\n} as const;\n\n/**\n * A composition of N color spots (one per color) with 2 types of\n * distortions applied to the coordinate space\n *\n * Uniforms:\n * - u_colors (vec4[]), u_colorsCount (float used as integer)\n * - u_distortion: warp distortion\n * - u_swirl: vortex distortion\n * - u_grainMixer: shape distortion\n * - u_grainOverlay: post-processing blending\n */\n\n// language=GLSL\nexport const meshGradientFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform vec4 u_colors[${meshGradientMeta.maxColorCount}];\nuniform float u_colorsCount;\n\nuniform float u_distortion;\nuniform float u_swirl;\nuniform float u_grainMixer;\nuniform float u_grainOverlay;\n\n${sizingVariablesDeclaration}\n${sizingDebugVariablesDeclaration}\n${sizingUniformsDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n${rotation2}\n${proceduralHash21}\n\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = hash21(i);\n float b = hash21(i + vec2(1.0, 0.0));\n float c = hash21(i + vec2(0.0, 1.0));\n float d = hash21(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\n\nfloat noise(vec2 n, vec2 seedOffset) {\n return valueNoise(n + seedOffset);\n}\n\nvec2 getPosition(int i, float t) {\n float a = float(i) * .37;\n float b = .6 + fract(float(i) / 3.) * .9;\n float c = .8 + fract(float(i + 1) / 4.);\n\n float x = sin(t * b + a);\n float y = cos(t * c + a * 1.5);\n\n return .5 + .5 * vec2(x, y);\n}\n\nvoid main() {\n vec2 shape_uv = v_objectUV;\n shape_uv += .5;\n\n vec2 grainUV = v_objectUV;\n // apply inverse transform to grain_uv so it respects the originXY\n float grainUVRot = u_rotation * 3.14159265358979323846 / 180.;\n mat2 graphicRotation = mat2(cos(grainUVRot), sin(grainUVRot), -sin(grainUVRot), cos(grainUVRot));\n vec2 graphicOffset = vec2(-u_offsetX, u_offsetY);\n grainUV = transpose(graphicRotation) * grainUV;\n grainUV *= u_scale;\n grainUV *= .7;\n grainUV -= graphicOffset;\n grainUV *= v_objectBoxSize;\n \n float grain = noise(grainUV, vec2(0.));\n float mixerGrain = .4 * u_grainMixer * (grain - .5);\n\n const float firstFrameOffset = 41.5;\n float t = .5 * (u_time + firstFrameOffset);\n\n float radius = smoothstep(0., 1., length(shape_uv - .5));\n float center = 1. - radius;\n for (float i = 1.; i <= 2.; i++) {\n shape_uv.x += u_distortion * center / i * sin(t + i * .4 * smoothstep(.0, 1., shape_uv.y)) * cos(.2 * t + i * 2.4 * smoothstep(.0, 1., shape_uv.y));\n shape_uv.y += u_distortion * center / i * cos(t + i * 2. * smoothstep(.0, 1., shape_uv.x));\n }\n\n vec2 uvRotated = shape_uv;\n uvRotated -= vec2(.5);\n float angle = 3. * u_swirl * radius;\n uvRotated = rotate(uvRotated, -angle);\n uvRotated += vec2(.5);\n\n vec3 color = vec3(0.);\n float opacity = 0.;\n float totalWeight = 0.;\n\n for (int i = 0; i < ${meshGradientMeta.maxColorCount}; i++) {\n if (i >= int(u_colorsCount)) break;\n\n vec2 pos = getPosition(i, t) + mixerGrain;\n vec3 colorFraction = u_colors[i].rgb * u_colors[i].a;\n float opacityFraction = u_colors[i].a;\n\n float dist = length(uvRotated - pos);\n\n dist = pow(dist, 3.5);\n float weight = 1. / (dist + 1e-3);\n color += colorFraction * weight;\n opacity += opacityFraction * weight;\n totalWeight += weight;\n }\n\n color /= max(1e-4, totalWeight);\n opacity /= max(1e-4, totalWeight);\n\n float rr = noise(rotate(grainUV, 1.), vec2(3.));\n float gg = noise(rotate(grainUV, 2.) + 10., vec2(-1.));\n float bb = noise(grainUV - 2., vec2(5.));\n vec3 grainColor = vec3(rr, gg, bb);\n color = mix(color, grainColor, .01 + .3 * u_grainOverlay);\n \n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface MeshGradientUniforms extends ShaderSizingUniforms {\n u_colors: vec4[];\n u_colorsCount: number;\n u_distortion: number;\n u_swirl: number;\n u_grainMixer: number;\n u_grainOverlay: number;\n}\n\nexport interface MeshGradientParams extends ShaderSizingParams, ShaderMotionParams {\n colors?: string[];\n distortion?: number;\n swirl?: number;\n grainMixer?: number;\n grainOverlay?: number;\n}\n"],
|
|
5
5
|
"mappings": ";;;;;AAEA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,WAAW,WAAW,wBAAwB;AAEhD,MAAM,mBAAmB;AAAA,EAC9B,eAAe;AACjB;AAeO,MAAM,6BAAqC;AAAA;AAAA;AAAA;AAAA;AAAA,wBAK1B,iBAAiB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQpD,0BAA0B;AAAA,EAC1B,+BAA+B;AAAA,EAC/B,yBAAyB;AAAA;AAAA;AAAA;AAAA,EAIzB,SAAS;AAAA,EACT,SAAS;AAAA,EACT,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAoEM,iBAAiB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/shaders/neuro-noise.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { rotation2, colorBandingFix } from '../shader-utils.js';\n\n/**\n *\n * Fractal-like structure made of several layers of sine arches\n * Original algorithm: https://x.com/zozuar/status/1625182758745128981/\n *\n * Uniforms:\n * - u_colorBack, u_colorMid, u_colorFront (RGBA)\n * - u_brightness, u_contrast\n *\n */\n\n// language=GLSL\nexport const neuroNoiseFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\nuniform vec2 u_resolution;\nuniform float u_pixelRatio;\n\nuniform vec4 u_colorFront;\nuniform vec4 u_colorMid;\nuniform vec4 u_colorBack;\nuniform float u_brightness;\nuniform float u_contrast;\n\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${rotation2}\n\nfloat neuroShape(vec2 uv, float t) {\n vec2 sine_acc = vec2(0.);\n vec2 res = vec2(0.);\n float scale = 8.;\n\n for (int j = 0; j < 15; j++) {\n uv = rotate(uv, 1.);\n sine_acc = rotate(sine_acc, 1.);\n vec2 layer = uv * scale + float(j) + sine_acc - t;\n sine_acc += sin(layer);\n res += (.5 + .5 * cos(layer)) / scale;\n scale *= (1.2);\n }\n return res.x + res.y;\n}\n\nvoid main() {\n vec2 shape_uv = v_patternUV;\n shape_uv *= .13;\n\n float t = .5 * u_time;\n\n float noise = neuroShape(shape_uv, t);\n\n noise = (1. + u_brightness) *
|
|
4
|
+
"sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { rotation2, colorBandingFix } from '../shader-utils.js';\n\n/**\n *\n * Fractal-like structure made of several layers of sine arches\n * Original algorithm: https://x.com/zozuar/status/1625182758745128981/\n *\n * Uniforms:\n * - u_colorBack, u_colorMid, u_colorFront (RGBA)\n * - u_brightness, u_contrast\n *\n */\n\n// language=GLSL\nexport const neuroNoiseFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\nuniform vec2 u_resolution;\nuniform float u_pixelRatio;\n\nuniform vec4 u_colorFront;\nuniform vec4 u_colorMid;\nuniform vec4 u_colorBack;\nuniform float u_brightness;\nuniform float u_contrast;\n\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${rotation2}\n\nfloat neuroShape(vec2 uv, float t) {\n vec2 sine_acc = vec2(0.);\n vec2 res = vec2(0.);\n float scale = 8.;\n\n for (int j = 0; j < 15; j++) {\n uv = rotate(uv, 1.);\n sine_acc = rotate(sine_acc, 1.);\n vec2 layer = uv * scale + float(j) + sine_acc - t;\n sine_acc += sin(layer);\n res += (.5 + .5 * cos(layer)) / scale;\n scale *= (1.2);\n }\n return res.x + res.y;\n}\n\nvoid main() {\n vec2 shape_uv = v_patternUV;\n shape_uv *= .13;\n\n float t = .5 * u_time;\n\n float noise = neuroShape(shape_uv, t);\n\n noise = (1. + u_brightness) * noise * noise;\n noise = pow(noise, .7 + 6. * u_contrast);\n noise = min(1.4, noise);\n\n float blend = smoothstep(0.7, 1.4, noise);\n\n vec4 frontC = u_colorFront;\n frontC.rgb *= frontC.a;\n vec4 midC = u_colorMid;\n midC.rgb *= midC.a;\n vec4 blendFront = mix(midC, frontC, blend);\n\n float safeNoise = max(noise, 0.0);\n vec3 color = blendFront.rgb * safeNoise;\n float opacity = clamp(blendFront.a * safeNoise, 0., 1.);\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n color = color + bgColor * (1. - opacity);\n opacity = opacity + u_colorBack.a * (1. - opacity);\n\n ${colorBandingFix}\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface NeuroNoiseUniforms extends ShaderSizingUniforms {\n u_colorFront: [number, number, number, number];\n u_colorMid: [number, number, number, number];\n u_colorBack: [number, number, number, number];\n u_brightness: number;\n u_contrast: number;\n}\n\nexport interface NeuroNoiseParams extends ShaderSizingParams, ShaderMotionParams {\n colorFront?: string;\n colorMid?: string;\n colorBack?: string;\n brightness?: number;\n contrast?: number;\n}\n"],
|
|
5
5
|
"mappings": ";;;;;AACA,SAAS,kCAAsF;AAC/F,SAAS,WAAW,uBAAuB;AAcpC,MAAM,2BAAmC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAc9C,0BAA0B;AAAA;AAAA;AAAA;AAAA,EAI1B,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA8CP,eAAe;AAAA;AAAA;AAAA;AAAA;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -40,9 +40,9 @@ float getUvFrame(vec2 uv) {
|
|
|
40
40
|
float aay = 2. * fwidth(uv.y);
|
|
41
41
|
|
|
42
42
|
float left = smoothstep(0., aax, uv.x);
|
|
43
|
-
float right
|
|
43
|
+
float right = 1. - smoothstep(1. - aax, 1., uv.x);
|
|
44
44
|
float bottom = smoothstep(0., aay, uv.y);
|
|
45
|
-
float top
|
|
45
|
+
float top = 1. - smoothstep(1. - aay, 1., uv.y);
|
|
46
46
|
|
|
47
47
|
return left * right * bottom * top;
|
|
48
48
|
}
|
|
@@ -114,7 +114,7 @@ float crumpledNoise(vec2 t, float pw) {
|
|
|
114
114
|
wsum += w;
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
|
-
return pow(cl / wsum, .5) * 2.;
|
|
117
|
+
return pow(wsum != 0.0 ? cl / wsum : 0.0, .5) * 2.;
|
|
118
118
|
}
|
|
119
119
|
float crumplesShape(vec2 uv) {
|
|
120
120
|
return crumpledNoise(uv * .25, 16.) * crumpledNoise(uv * .5, 2.);
|
|
@@ -189,7 +189,7 @@ void main() {
|
|
|
189
189
|
float drops = u_drops * drops(patternUV * 2.);
|
|
190
190
|
|
|
191
191
|
float fade = u_fade * fbm(.17 * patternUV + 10. * u_seed);
|
|
192
|
-
fade = clamp(8. *
|
|
192
|
+
fade = clamp(8. * fade * fade * fade, 0., 1.);
|
|
193
193
|
|
|
194
194
|
w = mix(w, vec2(0.), fade);
|
|
195
195
|
w2 = mix(w2, vec2(0.), fade);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/shaders/paper-texture.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { rotation2, declarePI, fiberNoise, textureRandomizerR } from '../shader-utils.js';\n\n/**\n * Mimicking paper texture with a combination of noises\n *\n * Uniforms:\n * - u_colorFront, u_colorBack (RGBA)\n * - u_contrast - mixing front and back colors\n * - u_roughness - pixel noise, related to canvas => not scalable\n * - u_fiber, u_fiberSize - curly shaped noise\n * - u_crumples, u_crumpleSize - cell-based pattern\n * - u_folds, u_foldCount - lines pattern, 15 max\n * - u_drops - metaballs-like pattern\n * - u_seed - applied to folds, crumples and dots\n * - u_fade - big-scale noise mask applied to everything but roughness\n *\n * - u_noiseTexture (sampler2D): pre-computed randomizer source\n *\n */\n\n// language=GLSL\nexport const paperTextureFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform vec2 u_resolution;\nuniform float u_pixelRatio;\n\nuniform vec4 u_colorFront;\nuniform vec4 u_colorBack;\n\nuniform sampler2D u_image;\nuniform float u_imageAspectRatio;\n\nuniform float u_contrast;\nuniform float u_roughness;\nuniform float u_fiber;\nuniform float u_fiberSize;\nuniform float u_crumples;\nuniform float u_crumpleSize;\nuniform float u_folds;\nuniform float u_foldCount;\nuniform float u_drops;\nuniform float u_seed;\nuniform float u_fade;\n\nuniform sampler2D u_noiseTexture;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\nfloat getUvFrame(vec2 uv) {\n float aax = 2. * fwidth(uv.x);\n float aay = 2. * fwidth(uv.y);\n\n float left = smoothstep(0., aax, uv.x);\n float right
|
|
4
|
+
"sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { rotation2, declarePI, fiberNoise, textureRandomizerR } from '../shader-utils.js';\n\n/**\n * Mimicking paper texture with a combination of noises\n *\n * Uniforms:\n * - u_colorFront, u_colorBack (RGBA)\n * - u_contrast - mixing front and back colors\n * - u_roughness - pixel noise, related to canvas => not scalable\n * - u_fiber, u_fiberSize - curly shaped noise\n * - u_crumples, u_crumpleSize - cell-based pattern\n * - u_folds, u_foldCount - lines pattern, 15 max\n * - u_drops - metaballs-like pattern\n * - u_seed - applied to folds, crumples and dots\n * - u_fade - big-scale noise mask applied to everything but roughness\n *\n * - u_noiseTexture (sampler2D): pre-computed randomizer source\n *\n */\n\n// language=GLSL\nexport const paperTextureFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform vec2 u_resolution;\nuniform float u_pixelRatio;\n\nuniform vec4 u_colorFront;\nuniform vec4 u_colorBack;\n\nuniform sampler2D u_image;\nuniform float u_imageAspectRatio;\n\nuniform float u_contrast;\nuniform float u_roughness;\nuniform float u_fiber;\nuniform float u_fiberSize;\nuniform float u_crumples;\nuniform float u_crumpleSize;\nuniform float u_folds;\nuniform float u_foldCount;\nuniform float u_drops;\nuniform float u_seed;\nuniform float u_fade;\n\nuniform sampler2D u_noiseTexture;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\nfloat getUvFrame(vec2 uv) {\n float aax = 2. * fwidth(uv.x);\n float aay = 2. * fwidth(uv.y);\n\n float left = smoothstep(0., aax, uv.x);\n float right = 1. - smoothstep(1. - aax, 1., uv.x);\n float bottom = smoothstep(0., aay, uv.y);\n float top = 1. - smoothstep(1. - aay, 1., uv.y);\n\n return left * right * bottom * top;\n}\n\n${declarePI}\n${rotation2}\n${textureRandomizerR}\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = randomR(i);\n float b = randomR(i + vec2(1.0, 0.0));\n float c = randomR(i + vec2(0.0, 1.0));\n float d = randomR(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\nfloat fbm(vec2 n) {\n float total = 0.0, amplitude = .4;\n for (int i = 0; i < 3; i++) {\n total += valueNoise(n) * amplitude;\n n *= 1.99;\n amplitude *= 0.65;\n }\n return total;\n}\n\n\nfloat randomG(vec2 p) {\n vec2 uv = floor(p) / 50. + .5;\n return texture(u_noiseTexture, fract(uv)).g;\n}\nfloat roughness(vec2 p) {\n p *= .1;\n float o = 0.;\n for (float i = 0.; ++i < 4.; p *= 2.1) {\n vec4 w = vec4(floor(p), ceil(p));\n vec2 f = fract(p);\n o += mix(\n mix(randomG(w.xy), randomG(w.xw), f.y),\n mix(randomG(w.zy), randomG(w.zw), f.y),\n f.x);\n o += .2 / exp(2. * abs(sin(.2 * p.x + .5 * p.y)));\n }\n return o / 3.;\n}\n\n${fiberNoise}\n\nvec2 randomGB(vec2 p) {\n vec2 uv = floor(p) / 50. + .5;\n return texture(u_noiseTexture, fract(uv)).gb;\n}\nfloat crumpledNoise(vec2 t, float pw) {\n vec2 p = floor(t);\n float wsum = 0.;\n float cl = 0.;\n for (int y = -1; y < 2; y += 1) {\n for (int x = -1; x < 2; x += 1) {\n vec2 b = vec2(float(x), float(y));\n vec2 q = b + p;\n vec2 q2 = q - floor(q / 8.) * 8.;\n vec2 c = q + randomGB(q2);\n vec2 r = c - t;\n float w = pow(smoothstep(0., 1., 1. - abs(r.x)), pw) * pow(smoothstep(0., 1., 1. - abs(r.y)), pw);\n cl += (.5 + .5 * sin((q2.x + q2.y * 5.) * 8.)) * w;\n wsum += w;\n }\n }\n return pow(wsum != 0.0 ? cl / wsum : 0.0, .5) * 2.;\n}\nfloat crumplesShape(vec2 uv) {\n return crumpledNoise(uv * .25, 16.) * crumpledNoise(uv * .5, 2.);\n}\n\n\nvec2 folds(vec2 uv) {\n vec3 pp = vec3(0.);\n float l = 9.;\n for (float i = 0.; i < 15.; i++) {\n if (i >= u_foldCount) break;\n vec2 rand = randomGB(vec2(i, i * u_seed));\n float an = rand.x * TWO_PI;\n vec2 p = vec2(cos(an), sin(an)) * rand.y;\n float dist = distance(uv, p);\n l = min(l, dist);\n\n if (l == dist) {\n pp.xy = (uv - p.xy);\n pp.z = dist;\n }\n }\n return mix(pp.xy, vec2(0.), pow(pp.z, .25));\n}\n\nfloat drops(vec2 uv) {\n vec2 iDropsUV = floor(uv);\n vec2 fDropsUV = fract(uv);\n float dropsMinDist = 1.;\n for (int j = -1; j <= 1; j++) {\n for (int i = -1; i <= 1; i++) {\n vec2 neighbor = vec2(float(i), float(j));\n vec2 offset = randomGB(iDropsUV + neighbor);\n offset = .5 + .5 * sin(10. * u_seed + TWO_PI * offset);\n vec2 pos = neighbor + offset - fDropsUV;\n float dist = length(pos);\n dropsMinDist = min(dropsMinDist, dropsMinDist*dist);\n }\n }\n return 1. - smoothstep(.05, .09, pow(dropsMinDist, .5));\n}\n\nfloat lst(float edge0, float edge1, float x) {\n return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);\n}\n\nvoid main() {\n\n vec2 imageUV = v_imageUV;\n vec2 patternUV = v_imageUV - .5;\n patternUV = 5. * (patternUV * vec2(u_imageAspectRatio, 1.));\n\n vec2 roughnessUv = 1.5 * (gl_FragCoord.xy - .5 * u_resolution) / u_pixelRatio;\n float roughness = roughness(roughnessUv + vec2(1., 0.)) - roughness(roughnessUv - vec2(1., 0.));\n\n vec2 crumplesUV = fract(patternUV * .02 / u_crumpleSize - u_seed) * 32.;\n float crumples = u_crumples * (crumplesShape(crumplesUV + vec2(.05, 0.)) - crumplesShape(crumplesUV));\n\n vec2 fiberUV = 2. / u_fiberSize * patternUV;\n float fiber = fiberNoise(fiberUV, vec2(0.));\n fiber = .5 * u_fiber * (fiber - 1.);\n\n vec2 normal = vec2(0.);\n vec2 normalImage = vec2(0.);\n\n vec2 foldsUV = patternUV * .12;\n foldsUV = rotate(foldsUV, 4. * u_seed);\n vec2 w = folds(foldsUV);\n foldsUV = rotate(foldsUV + .007 * cos(u_seed), .01 * sin(u_seed));\n vec2 w2 = folds(foldsUV);\n\n float drops = u_drops * drops(patternUV * 2.);\n\n float fade = u_fade * fbm(.17 * patternUV + 10. * u_seed);\n fade = clamp(8. * fade * fade * fade, 0., 1.);\n\n w = mix(w, vec2(0.), fade);\n w2 = mix(w2, vec2(0.), fade);\n crumples = mix(crumples, 0., fade);\n drops = mix(drops, 0., fade);\n fiber *= mix(1., .5, fade);\n roughness *= mix(1., .5, fade);\n\n normal.xy += u_folds * min(5. * u_contrast, 1.) * 4. * max(vec2(0.), w + w2);\n normalImage.xy += u_folds * 2. * w;\n\n normal.xy += crumples;\n normalImage.xy += 1.5 * crumples;\n\n normal.xy += 3. * drops;\n normalImage.xy += .2 * drops;\n\n normal.xy += u_roughness * 1.5 * roughness;\n normal.xy += fiber;\n\n normalImage += u_roughness * .75 * roughness;\n normalImage += .2 * fiber;\n\n vec3 lightPos = vec3(1., 2., 1.);\n float res = dot(normalize(vec3(normal, 9.5 - 9. * pow(u_contrast, .1))), normalize(lightPos));\n\n vec3 fgColor = u_colorFront.rgb * u_colorFront.a;\n float fgOpacity = u_colorFront.a;\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n float bgOpacity = u_colorBack.a;\n\n imageUV += .02 * normalImage;\n float frame = getUvFrame(imageUV);\n vec4 image = texture(u_image, imageUV);\n image.rgb += .6 * pow(u_contrast, .4) * (res - .7);\n\n frame *= image.a;\n\n vec3 color = fgColor * res;\n float opacity = fgOpacity * res;\n\n color += bgColor * (1. - opacity);\n opacity += bgOpacity * (1. - opacity);\n opacity = mix(opacity, 1., frame);\n\n color -= .007 * drops;\n\n color.rgb = mix(color, image.rgb, frame);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface PaperTextureUniforms extends ShaderSizingUniforms {\n u_image: HTMLImageElement | string | undefined;\n u_noiseTexture?: HTMLImageElement;\n u_colorFront: [number, number, number, number];\n u_colorBack: [number, number, number, number];\n u_contrast: number;\n u_roughness: number;\n u_fiber: number;\n u_fiberSize: number;\n u_crumples: number;\n u_foldCount: number;\n u_folds: number;\n u_fade: number;\n u_crumpleSize: number;\n u_drops: number;\n u_seed: number;\n}\n\nexport interface PaperTextureParams extends ShaderSizingParams, ShaderMotionParams {\n image?: HTMLImageElement | string;\n colorFront?: string;\n colorBack?: string;\n contrast?: number;\n roughness?: number;\n fiber?: number;\n fiberSize?: number;\n crumples?: number;\n foldCount?: number;\n folds?: number;\n fade?: number;\n crumpleSize?: number;\n drops?: number;\n seed?: number;\n}\n"],
|
|
5
5
|
"mappings": ";;;;;AACA,SAAS,kCAAsF;AAC/F,SAAS,WAAW,WAAW,YAAY,0BAA0B;AAqB9D,MAAM,6BAAqC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0BhD,0BAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgB1B,SAAS;AAAA,EACT,SAAS;AAAA,EACT,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA2ClB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -148,7 +148,7 @@ float get_max_amp(float persistence, float octaveCount) {
|
|
|
148
148
|
return octaveCount;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
return (1.0 - pow(persistence, octaveCount)) / (1.0 - persistence);
|
|
151
|
+
return (1.0 - pow(persistence, octaveCount)) / max(1e-4, (1.0 - persistence));
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
void main() {
|
|
@@ -164,7 +164,7 @@ void main() {
|
|
|
164
164
|
float noise = p_noise(p, int(octCount), persistence, u_lacunarity);
|
|
165
165
|
|
|
166
166
|
float max_amp = get_max_amp(persistence, octCount);
|
|
167
|
-
float noise_normalized = clamp((noise + max_amp) / (2. * max_amp) + (u_proportion - .5), 0.0, 1.0);
|
|
167
|
+
float noise_normalized = clamp((noise + max_amp) / max(1e-4, (2. * max_amp)) + (u_proportion - .5), 0.0, 1.0);
|
|
168
168
|
float sharpness = clamp(u_softness, 0., 1.);
|
|
169
169
|
float smooth_w = 0.5 * max(fwidth(noise_normalized), 0.001);
|
|
170
170
|
float res = smoothstep(
|