@paper-design/shaders 0.0.0-canary.390a3d9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +7 -0
- package/dist/get-empty-pixel.d.ts +1 -0
- package/dist/get-empty-pixel.js +19 -0
- package/dist/get-empty-pixel.js.map +7 -0
- package/dist/get-shader-color-from-string.d.ts +3 -0
- package/dist/get-shader-color-from-string.js +93 -0
- package/dist/get-shader-color-from-string.js.map +7 -0
- package/dist/get-shader-noise-texture.d.ts +1 -0
- package/dist/get-shader-noise-texture.js +19 -0
- package/dist/get-shader-noise-texture.js.map +7 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.js +168 -0
- package/dist/index.js.map +7 -0
- package/dist/shader-color-spaces.d.ts +6 -0
- package/dist/shader-color-spaces.js +96 -0
- package/dist/shader-color-spaces.js.map +7 -0
- package/dist/shader-mount.d.ts +130 -0
- package/dist/shader-mount.js +483 -0
- package/dist/shader-mount.js.map +7 -0
- package/dist/shader-sizing.d.ts +35 -0
- package/dist/shader-sizing.js +230 -0
- package/dist/shader-sizing.js.map +7 -0
- package/dist/shader-utils.d.ts +10 -0
- package/dist/shader-utils.js +131 -0
- package/dist/shader-utils.js.map +7 -0
- package/dist/shaders/color-panels.d.ts +50 -0
- package/dist/shaders/color-panels.js +224 -0
- package/dist/shaders/color-panels.js.map +7 -0
- package/dist/shaders/dithering.d.ts +56 -0
- package/dist/shaders/dithering.js +223 -0
- package/dist/shaders/dithering.js.map +7 -0
- package/dist/shaders/dot-grid.d.ts +51 -0
- package/dist/shaders/dot-grid.js +110 -0
- package/dist/shaders/dot-grid.js.map +7 -0
- package/dist/shaders/dot-orbit.d.ts +39 -0
- package/dist/shaders/dot-orbit.js +124 -0
- package/dist/shaders/dot-orbit.js.map +7 -0
- package/dist/shaders/fluted-glass.d.ts +73 -0
- package/dist/shaders/fluted-glass.js +187 -0
- package/dist/shaders/fluted-glass.js.map +7 -0
- package/dist/shaders/god-rays.d.ts +45 -0
- package/dist/shaders/god-rays.js +133 -0
- package/dist/shaders/god-rays.js.map +7 -0
- package/dist/shaders/grain-gradient.d.ts +58 -0
- package/dist/shaders/grain-gradient.js +289 -0
- package/dist/shaders/grain-gradient.js.map +7 -0
- package/dist/shaders/heatmap.d.ts +31 -0
- package/dist/shaders/heatmap.js +303 -0
- package/dist/shaders/heatmap.js.map +7 -0
- package/dist/shaders/image-dithering.d.ts +49 -0
- package/dist/shaders/image-dithering.js +163 -0
- package/dist/shaders/image-dithering.js.map +7 -0
- package/dist/shaders/image-halftone-dots.d.ts +46 -0
- package/dist/shaders/image-halftone-dots.js +337 -0
- package/dist/shaders/image-halftone-dots.js.map +7 -0
- package/dist/shaders/liquid-metal.d.ts +66 -0
- package/dist/shaders/liquid-metal.js +627 -0
- package/dist/shaders/liquid-metal.js.map +7 -0
- package/dist/shaders/mesh-gradient.d.ts +33 -0
- package/dist/shaders/mesh-gradient.js +136 -0
- package/dist/shaders/mesh-gradient.js.map +7 -0
- package/dist/shaders/metaballs.d.ts +35 -0
- package/dist/shaders/metaballs.js +112 -0
- package/dist/shaders/metaballs.js.map +7 -0
- package/dist/shaders/neuro-noise.d.ts +27 -0
- package/dist/shaders/neuro-noise.js +80 -0
- package/dist/shaders/neuro-noise.js.map +7 -0
- package/dist/shaders/paper-texture.d.ts +53 -0
- package/dist/shaders/paper-texture.js +248 -0
- package/dist/shaders/paper-texture.js.map +7 -0
- package/dist/shaders/perlin-noise.d.ts +33 -0
- package/dist/shaders/perlin-noise.js +195 -0
- package/dist/shaders/perlin-noise.js.map +7 -0
- package/dist/shaders/pulsing-border.d.ts +72 -0
- package/dist/shaders/pulsing-border.js +237 -0
- package/dist/shaders/pulsing-border.js.map +7 -0
- package/dist/shaders/simplex-noise.d.ts +27 -0
- package/dist/shaders/simplex-noise.js +100 -0
- package/dist/shaders/simplex-noise.js.map +7 -0
- package/dist/shaders/smoke-ring.d.ts +39 -0
- package/dist/shaders/smoke-ring.js +130 -0
- package/dist/shaders/smoke-ring.js.map +7 -0
- package/dist/shaders/spiral.d.ts +41 -0
- package/dist/shaders/spiral.js +81 -0
- package/dist/shaders/spiral.js.map +7 -0
- package/dist/shaders/static-mesh-gradient.d.ts +42 -0
- package/dist/shaders/static-mesh-gradient.js +139 -0
- package/dist/shaders/static-mesh-gradient.js.map +7 -0
- package/dist/shaders/static-radial-gradient.d.ts +50 -0
- package/dist/shaders/static-radial-gradient.js +195 -0
- package/dist/shaders/static-radial-gradient.js.map +7 -0
- package/dist/shaders/swirl.d.ts +42 -0
- package/dist/shaders/swirl.js +102 -0
- package/dist/shaders/swirl.js.map +7 -0
- package/dist/shaders/voronoi.d.ts +44 -0
- package/dist/shaders/voronoi.js +139 -0
- package/dist/shaders/voronoi.js.map +7 -0
- package/dist/shaders/warp.d.ts +54 -0
- package/dist/shaders/warp.js +132 -0
- package/dist/shaders/warp.js.map +7 -0
- package/dist/shaders/water.d.ts +39 -0
- package/dist/shaders/water.js +120 -0
- package/dist/shaders/water.js.map +7 -0
- package/dist/shaders/waves.d.ts +38 -0
- package/dist/shaders/waves.js +66 -0
- package/dist/shaders/waves.js.map +7 -0
- package/dist/types.d.ts +3 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +7 -0
- package/dist/vertex-shader.d.ts +2 -0
- package/dist/vertex-shader.js +211 -0
- package/dist/vertex-shader.js.map +7 -0
- package/package.json +27 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
2
|
+
* Paper Shaders *
|
|
3
|
+
* https://github.com/paper-design/shaders *
|
|
4
|
+
* * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
5
|
+
|
|
6
|
+
const heatmapMeta = {
|
|
7
|
+
maxColorCount: 10
|
|
8
|
+
};
|
|
9
|
+
const heatmapFragmentShader = `#version 300 es
|
|
10
|
+
precision highp float;
|
|
11
|
+
|
|
12
|
+
in mediump vec2 v_imageUV;
|
|
13
|
+
in mediump vec2 v_objectUV;
|
|
14
|
+
out vec4 fragColor;
|
|
15
|
+
|
|
16
|
+
uniform sampler2D u_image;
|
|
17
|
+
uniform float u_time;
|
|
18
|
+
uniform mediump float u_imageAspectRatio;
|
|
19
|
+
|
|
20
|
+
uniform vec4 u_colorBack;
|
|
21
|
+
uniform vec4 u_colors[${heatmapMeta.maxColorCount}];
|
|
22
|
+
uniform float u_colorsCount;
|
|
23
|
+
|
|
24
|
+
uniform float u_angle;
|
|
25
|
+
uniform float u_noise;
|
|
26
|
+
uniform float u_innerGlow;
|
|
27
|
+
uniform float u_outerGlow;
|
|
28
|
+
uniform float u_contour;
|
|
29
|
+
|
|
30
|
+
#define TWO_PI 6.28318530718
|
|
31
|
+
#define PI 3.14159265358979323846
|
|
32
|
+
|
|
33
|
+
float getImgFrame(vec2 uv, float th) {
|
|
34
|
+
float frame = 1.;
|
|
35
|
+
frame *= smoothstep(0., th, uv.y);
|
|
36
|
+
frame *= 1. - smoothstep(1. - th, 1., uv.y);
|
|
37
|
+
frame *= smoothstep(0., th, uv.x);
|
|
38
|
+
frame *= 1. - smoothstep(1. - th, 1., uv.x);
|
|
39
|
+
return frame;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
float circle(vec2 uv, vec2 c, vec2 r) {
|
|
43
|
+
return 1. - smoothstep(r[0], r[1], length(uv - c));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
float lst(float edge0, float edge1, float x) {
|
|
47
|
+
return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
float sst(float edge0, float edge1, float x) {
|
|
51
|
+
return smoothstep(edge0, edge1, x);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
float shadowShape(vec2 uv, float t, float contour) {
|
|
55
|
+
vec2 scaledUV = uv;
|
|
56
|
+
|
|
57
|
+
// base shape tranjectory
|
|
58
|
+
float posY = mix(-1., 2., t);
|
|
59
|
+
|
|
60
|
+
// scaleX when it's moving down
|
|
61
|
+
scaledUV.y -= .5;
|
|
62
|
+
float mainCircleScale = sst(0., .8, posY) * lst(1.4, .9, posY);
|
|
63
|
+
scaledUV *= vec2(1., 1. + 1.5 * mainCircleScale);
|
|
64
|
+
scaledUV.y += .5;
|
|
65
|
+
|
|
66
|
+
// base shape
|
|
67
|
+
float innerR = .4;
|
|
68
|
+
float outerR = 1. - .3 * (sst(.1, .2, t) * (1. - sst(.2, .5, t)));
|
|
69
|
+
float s = circle(scaledUV, vec2(.5, posY - .2), vec2(innerR, outerR));
|
|
70
|
+
float shapeSizing = sst(.2, .3, t) * sst(.6, .3, t);
|
|
71
|
+
s = pow(s, 1.4);
|
|
72
|
+
s *= 1.2;
|
|
73
|
+
|
|
74
|
+
// flat gradient to take over the shadow shape
|
|
75
|
+
float topFlattener = 0.;
|
|
76
|
+
{
|
|
77
|
+
float pos = posY - uv.y;
|
|
78
|
+
float edge = 1.2;
|
|
79
|
+
topFlattener = lst(-.4, 0., pos) * (1. - sst(.0, edge, pos));
|
|
80
|
+
topFlattener = pow(topFlattener, 3.);
|
|
81
|
+
float topFlattenerMixer = (1. - sst(.0, .3, pos));
|
|
82
|
+
s = mix(topFlattener, s, topFlattenerMixer);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// apple right circle
|
|
86
|
+
{
|
|
87
|
+
float visibility = sst(.6, .7, t) * (1. - sst(.8, .9, t));
|
|
88
|
+
float angle = -2. -t * TWO_PI;
|
|
89
|
+
float rightCircle = circle(uv, vec2(.95 - .2 * cos(angle), .4 - .1 * sin(angle)), vec2(.15, .3));
|
|
90
|
+
rightCircle *= visibility;
|
|
91
|
+
s = mix(s, 0., rightCircle);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// apple top circle
|
|
95
|
+
{
|
|
96
|
+
float topCircle = circle(uv, vec2(.5, .19), vec2(.05, .25));
|
|
97
|
+
topCircle += 2. * contour * circle(uv, vec2(.5, .19), vec2(.2, .5));
|
|
98
|
+
float visibility = .55 * sst(.2, .3, t) * (1. - sst(.3, .45, t));
|
|
99
|
+
topCircle *= visibility;
|
|
100
|
+
s = mix(s, 0., topCircle);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
float leafMask = circle(uv, vec2(.53, .13), vec2(.08, .19));
|
|
104
|
+
leafMask = mix(leafMask, 0., 1. - sst(.4, .54, uv.x));
|
|
105
|
+
leafMask = mix(0., leafMask, sst(.0, .2, uv.y));
|
|
106
|
+
leafMask *= (sst(.5, 1.1, posY) * sst(1.5, 1.3, posY));
|
|
107
|
+
s += leafMask;
|
|
108
|
+
|
|
109
|
+
// apple bottom circle
|
|
110
|
+
{
|
|
111
|
+
float visibility = sst(.0, .4, t) * (1. - sst(.6, .8, t));
|
|
112
|
+
s = mix(s, 0., visibility * circle(uv, vec2(.52, .92), vec2(.09, .25)));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// random balls that are invisible if apple logo is selected
|
|
116
|
+
{
|
|
117
|
+
float pos = sst(.0, .6, t) * (1. - sst(.6, 1., t));
|
|
118
|
+
s = mix(s, .5, circle(uv, vec2(.0, 1.2 - .5 * pos), vec2(.1, .3)));
|
|
119
|
+
s = mix(s, .0, circle(uv, vec2(1., .5 + .5 * pos), vec2(.1, .3)));
|
|
120
|
+
|
|
121
|
+
s = mix(s, 1., circle(uv, vec2(.95, .2 + .2 * sst(.3, .4, t) * sst(.7, .5, t)), vec2(.07, .22)));
|
|
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));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
s = clamp(0., 1., s);
|
|
127
|
+
return s;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
void main() {
|
|
132
|
+
vec2 uv = v_objectUV + .5;
|
|
133
|
+
uv.y = 1. - uv.y;
|
|
134
|
+
|
|
135
|
+
vec2 imgUV = v_imageUV;
|
|
136
|
+
imgUV -= .5;
|
|
137
|
+
imgUV *= 0.5714285714285714;
|
|
138
|
+
imgUV += .5;
|
|
139
|
+
float imgSoftFrame = getImgFrame(imgUV, .03);
|
|
140
|
+
|
|
141
|
+
vec4 img = texture(u_image, imgUV);
|
|
142
|
+
if (img.a == 0.) {
|
|
143
|
+
fragColor = u_colorBack;
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
float t = .1 * u_time;
|
|
148
|
+
t -= .3;
|
|
149
|
+
|
|
150
|
+
float tCopy = t + 1. / 3.;
|
|
151
|
+
float tCopy2 = t + 2. / 3.;
|
|
152
|
+
|
|
153
|
+
t = mod(t, 1.);
|
|
154
|
+
tCopy = mod(tCopy, 1.);
|
|
155
|
+
tCopy2 = mod(tCopy2, 1.);
|
|
156
|
+
|
|
157
|
+
vec2 animationUV = imgUV - vec2(.5);
|
|
158
|
+
float angle = -u_angle * PI / 180.;
|
|
159
|
+
float cosA = cos(angle);
|
|
160
|
+
float sinA = sin(angle);
|
|
161
|
+
animationUV = vec2(
|
|
162
|
+
animationUV.x * cosA - animationUV.y * sinA,
|
|
163
|
+
animationUV.x * sinA + animationUV.y * cosA
|
|
164
|
+
) + vec2(.5);
|
|
165
|
+
|
|
166
|
+
float shape = img[0];
|
|
167
|
+
float outerBlur = 1. - mix(1., img[1], shape);
|
|
168
|
+
float innerBlur = mix(img[1], 0., shape);
|
|
169
|
+
float contour = mix(img[2], 0., shape);
|
|
170
|
+
|
|
171
|
+
outerBlur *= imgSoftFrame;
|
|
172
|
+
|
|
173
|
+
float shadow = shadowShape(animationUV, t, innerBlur);
|
|
174
|
+
float shadowCopy = shadowShape(animationUV, tCopy, innerBlur);
|
|
175
|
+
float shadowCopy2 = shadowShape(animationUV, tCopy2, innerBlur);
|
|
176
|
+
|
|
177
|
+
float inner = .8 + .8 * innerBlur;
|
|
178
|
+
inner = mix(inner, 0., shadow);
|
|
179
|
+
inner = mix(inner, 0., shadowCopy);
|
|
180
|
+
inner = mix(inner, 0., shadowCopy2);
|
|
181
|
+
|
|
182
|
+
inner *= mix(0., 2., u_innerGlow);
|
|
183
|
+
|
|
184
|
+
inner += (u_contour * 2.) * contour;
|
|
185
|
+
inner = min(1., inner);
|
|
186
|
+
inner *= (1. - shape);
|
|
187
|
+
|
|
188
|
+
float outer = 0.;
|
|
189
|
+
{
|
|
190
|
+
t *= 3.;
|
|
191
|
+
t = mod(t - .1, 1.);
|
|
192
|
+
|
|
193
|
+
outer = .9 * pow(outerBlur, .8);
|
|
194
|
+
float y = mod(animationUV.y - t, 1.);
|
|
195
|
+
float animatedMask = sst(.3, .65, y) * (1. - sst(.65, 1., y));
|
|
196
|
+
animatedMask = .5 + animatedMask;
|
|
197
|
+
outer *= animatedMask;
|
|
198
|
+
outer *= mix(0., 5., pow(u_outerGlow, 2.));
|
|
199
|
+
outer *= imgSoftFrame;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
inner = pow(inner, 1.2);
|
|
203
|
+
float heat = clamp(inner + outer, 0., 1.);
|
|
204
|
+
|
|
205
|
+
heat += (.005 + .35 * u_noise) * (fract(sin(dot(uv, vec2(12.9898, 78.233))) * 43758.5453123) - .5);
|
|
206
|
+
|
|
207
|
+
float mixer = heat * u_colorsCount;
|
|
208
|
+
vec4 gradient = u_colors[0];
|
|
209
|
+
gradient.rgb *= gradient.a;
|
|
210
|
+
float outerShape = 0.;
|
|
211
|
+
for (int i = 1; i < ${heatmapMeta.maxColorCount + 1}; i++) {
|
|
212
|
+
if (i > int(u_colorsCount)) break;
|
|
213
|
+
float m = clamp(mixer - float(i - 1), 0., 1.);
|
|
214
|
+
if (i == 1) {
|
|
215
|
+
outerShape = m;
|
|
216
|
+
}
|
|
217
|
+
vec4 c = u_colors[i - 1];
|
|
218
|
+
c.rgb *= c.a;
|
|
219
|
+
gradient = mix(gradient, c, m);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
vec3 color = gradient.rgb * outerShape;
|
|
223
|
+
float opacity = gradient.a * outerShape;
|
|
224
|
+
|
|
225
|
+
vec3 bgColor = u_colorBack.rgb * u_colorBack.a;
|
|
226
|
+
color = color + bgColor * (1.0 - opacity);
|
|
227
|
+
opacity = opacity + u_colorBack.a * (1.0 - opacity);
|
|
228
|
+
|
|
229
|
+
color += .02 * (fract(sin(dot(uv + 1., vec2(12.9898, 78.233))) * 43758.5453123) - .5);
|
|
230
|
+
|
|
231
|
+
fragColor = vec4(color, opacity);
|
|
232
|
+
}
|
|
233
|
+
`;
|
|
234
|
+
function toProcessedHeatmap(file) {
|
|
235
|
+
const canvas = document.createElement("canvas");
|
|
236
|
+
const canvasSize = 1e3;
|
|
237
|
+
return new Promise((resolve, reject) => {
|
|
238
|
+
const image = new Image();
|
|
239
|
+
image.crossOrigin = "anonymous";
|
|
240
|
+
image.addEventListener("load", () => {
|
|
241
|
+
if (typeof file === "string" ? file.endsWith(".svg") : file.type === "image/svg+xml") {
|
|
242
|
+
image.width = canvasSize;
|
|
243
|
+
image.height = canvasSize;
|
|
244
|
+
}
|
|
245
|
+
const ratio = image.naturalWidth / image.naturalHeight;
|
|
246
|
+
const maxBlur = Math.floor(canvasSize * 0.15);
|
|
247
|
+
const padding = Math.ceil(maxBlur * 2.5);
|
|
248
|
+
let imgWidth = canvasSize;
|
|
249
|
+
let imgHeight = canvasSize;
|
|
250
|
+
if (ratio > 1) {
|
|
251
|
+
imgHeight = Math.floor(canvasSize / ratio);
|
|
252
|
+
} else {
|
|
253
|
+
imgWidth = Math.floor(canvasSize * ratio);
|
|
254
|
+
}
|
|
255
|
+
canvas.width = imgWidth + 2 * padding;
|
|
256
|
+
canvas.height = imgHeight + 2 * padding;
|
|
257
|
+
const ctx = canvas.getContext("2d", { willReadFrequently: true });
|
|
258
|
+
if (!ctx) {
|
|
259
|
+
throw new Error("Failed to get canvas 2d context");
|
|
260
|
+
}
|
|
261
|
+
ctx.fillStyle = "white";
|
|
262
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
263
|
+
ctx.filter = "grayscale(100%) blur(" + maxBlur + "px)";
|
|
264
|
+
ctx.drawImage(image, padding, padding, imgWidth, imgHeight);
|
|
265
|
+
const bigBlurData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
|
|
266
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
267
|
+
ctx.filter = "grayscale(100%) blur(" + Math.round(0.12 * maxBlur) + "px)";
|
|
268
|
+
ctx.drawImage(image, padding, padding, imgWidth, imgHeight);
|
|
269
|
+
const innerBlurSmallData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
|
|
270
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
271
|
+
ctx.filter = "grayscale(100%) blur(5px)";
|
|
272
|
+
ctx.drawImage(image, padding, padding, imgWidth, imgHeight);
|
|
273
|
+
const contourData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
|
|
274
|
+
let processedImageData = ctx.createImageData(canvas.width, canvas.height);
|
|
275
|
+
const totalPixels = canvas.width * canvas.height;
|
|
276
|
+
for (let i = 0; i < totalPixels; i++) {
|
|
277
|
+
const px = i * 4;
|
|
278
|
+
processedImageData.data[px] = contourData[px];
|
|
279
|
+
processedImageData.data[px + 1] = bigBlurData[px];
|
|
280
|
+
processedImageData.data[px + 2] = innerBlurSmallData[px];
|
|
281
|
+
processedImageData.data[px + 3] = 255;
|
|
282
|
+
}
|
|
283
|
+
ctx.putImageData(processedImageData, 0, 0);
|
|
284
|
+
canvas.toBlob((blob) => {
|
|
285
|
+
if (!blob) {
|
|
286
|
+
reject(new Error("Failed to create PNG blob"));
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
resolve({ blob });
|
|
290
|
+
}, "image/png");
|
|
291
|
+
});
|
|
292
|
+
image.addEventListener("error", () => {
|
|
293
|
+
reject(new Error("Failed to load image"));
|
|
294
|
+
});
|
|
295
|
+
image.src = typeof file === "string" ? file : URL.createObjectURL(file);
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
export {
|
|
299
|
+
heatmapFragmentShader,
|
|
300
|
+
heatmapMeta,
|
|
301
|
+
toProcessedHeatmap
|
|
302
|
+
};
|
|
303
|
+
//# sourceMappingURL=heatmap.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 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 *= 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
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ShaderMotionParams } from '../shader-mount.js';
|
|
2
|
+
import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';
|
|
3
|
+
/**
|
|
4
|
+
* Dithering effect over user texture using 3-color palette
|
|
5
|
+
* or colors sampled from original image
|
|
6
|
+
*
|
|
7
|
+
* Uniforms:
|
|
8
|
+
* - u_colorBack, u_colorFront, u_colorHighlight (RGBA)
|
|
9
|
+
* (u_colorHighlight to be the lightest parts of u_colorFront pixels)
|
|
10
|
+
* - pxSize: px size set relative to canvas resolution
|
|
11
|
+
* - u_type (float used as integer)
|
|
12
|
+
* ---- 1: random dithering
|
|
13
|
+
* ---- 2: 2x2 Bayer matrix
|
|
14
|
+
* ---- 3: 4x4 Bayer matrix
|
|
15
|
+
* ---- 4: 8x8 Bayer matrix
|
|
16
|
+
* - u_originalColors - switching between 3 colors palette and original image colors
|
|
17
|
+
* - u_colorSteps - number of colors to use (applies to both color modes)
|
|
18
|
+
*
|
|
19
|
+
* Note: pixelization is applied to the shapes BEFORE dithering,
|
|
20
|
+
* meaning pixels don't react to scaling and fit
|
|
21
|
+
*/
|
|
22
|
+
export declare const imageDitheringFragmentShader: string;
|
|
23
|
+
export interface ImageDitheringUniforms extends ShaderSizingUniforms {
|
|
24
|
+
u_image: HTMLImageElement | string;
|
|
25
|
+
u_colorFront: [number, number, number, number];
|
|
26
|
+
u_colorBack: [number, number, number, number];
|
|
27
|
+
u_colorHighlight: [number, number, number, number];
|
|
28
|
+
u_type: (typeof DitheringTypes)[DitheringType];
|
|
29
|
+
u_pxSize: number;
|
|
30
|
+
u_colorSteps: number;
|
|
31
|
+
u_originalColors: boolean;
|
|
32
|
+
}
|
|
33
|
+
export interface ImageDitheringParams extends ShaderSizingParams, ShaderMotionParams {
|
|
34
|
+
image: HTMLImageElement | string;
|
|
35
|
+
colorFront?: string;
|
|
36
|
+
colorBack?: string;
|
|
37
|
+
colorHighlight?: string;
|
|
38
|
+
type?: DitheringType;
|
|
39
|
+
size?: number;
|
|
40
|
+
colorSteps?: number;
|
|
41
|
+
originalColors?: boolean;
|
|
42
|
+
}
|
|
43
|
+
export declare const DitheringTypes: {
|
|
44
|
+
readonly random: 1;
|
|
45
|
+
readonly '2x2': 2;
|
|
46
|
+
readonly '4x4': 3;
|
|
47
|
+
readonly '8x8': 4;
|
|
48
|
+
};
|
|
49
|
+
export type DitheringType = keyof typeof DitheringTypes;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
2
|
+
* Paper Shaders *
|
|
3
|
+
* https://github.com/paper-design/shaders *
|
|
4
|
+
* * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
5
|
+
|
|
6
|
+
import { sizingUV } from "../shader-sizing.js";
|
|
7
|
+
import { proceduralHash21 } from "../shader-utils.js";
|
|
8
|
+
const imageDitheringFragmentShader = `#version 300 es
|
|
9
|
+
precision lowp float;
|
|
10
|
+
|
|
11
|
+
uniform mediump vec2 u_resolution;
|
|
12
|
+
uniform mediump float u_pixelRatio;
|
|
13
|
+
uniform mediump float u_originX;
|
|
14
|
+
uniform mediump float u_originY;
|
|
15
|
+
uniform mediump float u_worldWidth;
|
|
16
|
+
uniform mediump float u_worldHeight;
|
|
17
|
+
uniform mediump float u_fit;
|
|
18
|
+
|
|
19
|
+
uniform mediump float u_scale;
|
|
20
|
+
uniform mediump float u_rotation;
|
|
21
|
+
uniform mediump float u_offsetX;
|
|
22
|
+
uniform mediump float u_offsetY;
|
|
23
|
+
|
|
24
|
+
uniform vec4 u_colorFront;
|
|
25
|
+
uniform vec4 u_colorBack;
|
|
26
|
+
uniform vec4 u_colorHighlight;
|
|
27
|
+
|
|
28
|
+
uniform sampler2D u_image;
|
|
29
|
+
uniform mediump float u_imageAspectRatio;
|
|
30
|
+
|
|
31
|
+
uniform float u_type;
|
|
32
|
+
uniform float u_pxSize;
|
|
33
|
+
uniform bool u_originalColors;
|
|
34
|
+
uniform float u_colorSteps;
|
|
35
|
+
|
|
36
|
+
out vec4 fragColor;
|
|
37
|
+
|
|
38
|
+
float getUvFrame(vec2 uv, vec2 px) {
|
|
39
|
+
float left = step(-px.x, uv.x);
|
|
40
|
+
float right = step(uv.x, 1.);
|
|
41
|
+
float bottom = step(-px.y, uv.y);
|
|
42
|
+
float top = step(uv.y, 1. + px.y);
|
|
43
|
+
|
|
44
|
+
return left * right * bottom * top;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
${proceduralHash21}
|
|
48
|
+
|
|
49
|
+
const int bayer2x2[4] = int[4](0, 2, 3, 1);
|
|
50
|
+
const int bayer4x4[16] = int[16](
|
|
51
|
+
0, 8, 2, 10,
|
|
52
|
+
12, 4, 14, 6,
|
|
53
|
+
3, 11, 1, 9,
|
|
54
|
+
15, 7, 13, 5
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const int bayer8x8[64] = int[64](
|
|
58
|
+
0, 32, 8, 40, 2, 34, 10, 42,
|
|
59
|
+
48, 16, 56, 24, 50, 18, 58, 26,
|
|
60
|
+
12, 44, 4, 36, 14, 46, 6, 38,
|
|
61
|
+
60, 28, 52, 20, 62, 30, 54, 22,
|
|
62
|
+
3, 35, 11, 43, 1, 33, 9, 41,
|
|
63
|
+
51, 19, 59, 27, 49, 17, 57, 25,
|
|
64
|
+
15, 47, 7, 39, 13, 45, 5, 37,
|
|
65
|
+
63, 31, 55, 23, 61, 29, 53, 21
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
float getBayerValue(vec2 uv, int size) {
|
|
69
|
+
ivec2 pos = ivec2(fract(uv / float(size)) * float(size));
|
|
70
|
+
int index = pos.y * size + pos.x;
|
|
71
|
+
|
|
72
|
+
if (size == 2) {
|
|
73
|
+
return float(bayer2x2[index]) / 4.0;
|
|
74
|
+
} else if (size == 4) {
|
|
75
|
+
return float(bayer4x4[index]) / 16.0;
|
|
76
|
+
} else if (size == 8) {
|
|
77
|
+
return float(bayer8x8[index]) / 64.0;
|
|
78
|
+
}
|
|
79
|
+
return 0.0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
void main() {
|
|
84
|
+
|
|
85
|
+
#define USE_IMAGE_SIZING
|
|
86
|
+
#define USE_PIXELIZATION
|
|
87
|
+
${sizingUV}
|
|
88
|
+
|
|
89
|
+
vec2 dithering_uv = pxSizeUv;
|
|
90
|
+
vec2 ditheringNoise_uv = u_resolution * uv;
|
|
91
|
+
vec4 image = texture(u_image, imageUV);
|
|
92
|
+
float frame = getUvFrame(imageUV, pxSize / u_resolution.xy);
|
|
93
|
+
|
|
94
|
+
int type = int(floor(u_type));
|
|
95
|
+
float dithering = 0.0;
|
|
96
|
+
|
|
97
|
+
float lum = dot(vec3(.2126, .7152, .0722), image.rgb);
|
|
98
|
+
|
|
99
|
+
switch (type) {
|
|
100
|
+
case 1: {
|
|
101
|
+
dithering = step(hash21(ditheringNoise_uv), lum);
|
|
102
|
+
} break;
|
|
103
|
+
case 2:
|
|
104
|
+
dithering = getBayerValue(dithering_uv, 2);
|
|
105
|
+
break;
|
|
106
|
+
case 3:
|
|
107
|
+
dithering = getBayerValue(dithering_uv, 4);
|
|
108
|
+
break;
|
|
109
|
+
default:
|
|
110
|
+
dithering = getBayerValue(dithering_uv, 8);
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
float steps = max(floor(u_colorSteps), 1.);
|
|
116
|
+
float ditherAmount = 1.0 / (steps);
|
|
117
|
+
|
|
118
|
+
vec3 color = vec3(0.0);
|
|
119
|
+
float opacity = 1.;
|
|
120
|
+
|
|
121
|
+
dithering -= .5;
|
|
122
|
+
float brightness = clamp(lum + dithering * ditherAmount, 0.0, 1.0);
|
|
123
|
+
brightness = mix(0.0, brightness, frame);
|
|
124
|
+
float quantLum = floor(brightness * steps + 0.5) / steps;
|
|
125
|
+
|
|
126
|
+
if (u_originalColors == true) {
|
|
127
|
+
vec3 normColor = image.rgb / max(lum, 0.001);
|
|
128
|
+
color = normColor * quantLum;
|
|
129
|
+
|
|
130
|
+
float quantAlpha = floor(image.a * steps + 0.5) / steps;
|
|
131
|
+
opacity = mix(quantLum, 1., quantAlpha);
|
|
132
|
+
} else {
|
|
133
|
+
vec3 fgColor = u_colorFront.rgb * u_colorFront.a;
|
|
134
|
+
float fgOpacity = u_colorFront.a;
|
|
135
|
+
vec3 bgColor = u_colorBack.rgb * u_colorBack.a;
|
|
136
|
+
float bgOpacity = u_colorBack.a;
|
|
137
|
+
vec3 hlColor = u_colorHighlight.rgb * u_colorHighlight.a;
|
|
138
|
+
float hlOpacity = u_colorHighlight.a;
|
|
139
|
+
|
|
140
|
+
fgColor = mix(fgColor, hlColor, step(1.02 - .02 * u_colorSteps, brightness));
|
|
141
|
+
fgOpacity = mix(fgOpacity, hlOpacity, step(1.02 - .02 * u_colorSteps, brightness));
|
|
142
|
+
|
|
143
|
+
color = fgColor * quantLum;
|
|
144
|
+
opacity = fgOpacity * quantLum;
|
|
145
|
+
color += bgColor * (1.0 - opacity);
|
|
146
|
+
opacity += bgOpacity * (1.0 - opacity);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
fragColor = vec4(color, opacity);
|
|
151
|
+
}
|
|
152
|
+
`;
|
|
153
|
+
const DitheringTypes = {
|
|
154
|
+
"random": 1,
|
|
155
|
+
"2x2": 2,
|
|
156
|
+
"4x4": 3,
|
|
157
|
+
"8x8": 4
|
|
158
|
+
};
|
|
159
|
+
export {
|
|
160
|
+
DitheringTypes,
|
|
161
|
+
imageDitheringFragmentShader
|
|
162
|
+
};
|
|
163
|
+
//# sourceMappingURL=image-dithering.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 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 lowp 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
|
+
"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
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ShaderMotionParams } from '../shader-mount.js';
|
|
2
|
+
import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';
|
|
3
|
+
/**
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
Uniforms:
|
|
7
|
+
- u_colorBack, u_colorFront, u_colorHighlight (RGBA)
|
|
8
|
+
(u_colorHighlight to be the lighdiagonalGrid parts of u_colorFront pixels)
|
|
9
|
+
- size: px size set relative to canvas resolution
|
|
10
|
+
*/
|
|
11
|
+
export declare const imageHalftoneDotsFragmentShader: string;
|
|
12
|
+
export interface ImageHalftoneDotsUniforms extends ShaderSizingUniforms {
|
|
13
|
+
u_image: HTMLImageElement | string | undefined;
|
|
14
|
+
u_colorFront: [number, number, number, number];
|
|
15
|
+
u_colorBack: [number, number, number, number];
|
|
16
|
+
u_size: number;
|
|
17
|
+
u_diagonalGrid: boolean;
|
|
18
|
+
u_radius: number;
|
|
19
|
+
u_contrast: number;
|
|
20
|
+
u_originalColors: boolean;
|
|
21
|
+
u_inverted: boolean;
|
|
22
|
+
u_grainMixer: number;
|
|
23
|
+
u_grainOverlay: number;
|
|
24
|
+
u_type: (typeof ImageHalftoneDotsTypes)[ImageHalftoneDotsType];
|
|
25
|
+
}
|
|
26
|
+
export interface ImageHalftoneDotsParams extends ShaderSizingParams, ShaderMotionParams {
|
|
27
|
+
image?: HTMLImageElement | string;
|
|
28
|
+
colorFront?: string;
|
|
29
|
+
colorBack?: string;
|
|
30
|
+
size?: number;
|
|
31
|
+
diagonalGrid?: boolean;
|
|
32
|
+
radius?: number;
|
|
33
|
+
contrast?: number;
|
|
34
|
+
originalColors?: boolean;
|
|
35
|
+
inverted?: boolean;
|
|
36
|
+
grainMixer?: number;
|
|
37
|
+
grainOverlay?: number;
|
|
38
|
+
type?: ImageHalftoneDotsType;
|
|
39
|
+
}
|
|
40
|
+
export declare const ImageHalftoneDotsTypes: {
|
|
41
|
+
readonly classic: 0;
|
|
42
|
+
readonly hole: 1;
|
|
43
|
+
readonly gooey: 2;
|
|
44
|
+
readonly soft: 3;
|
|
45
|
+
};
|
|
46
|
+
export type ImageHalftoneDotsType = keyof typeof ImageHalftoneDotsTypes;
|