shaders-oneshot 0.0.81-shared.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +5 -0
  3. package/README.md +7 -0
  4. package/dist/empty-pixel.d.ts +1 -0
  5. package/dist/empty-pixel.js +10 -0
  6. package/dist/empty-pixel.js.map +7 -0
  7. package/dist/get-shader-color-from-string.d.ts +3 -0
  8. package/dist/get-shader-color-from-string.js +101 -0
  9. package/dist/get-shader-color-from-string.js.map +7 -0
  10. package/dist/get-shader-color-from-string.test.d.ts +1 -0
  11. package/dist/get-shader-color-from-string.test.js +69 -0
  12. package/dist/get-shader-noise-texture.d.ts +1 -0
  13. package/dist/get-shader-noise-texture.js +18 -0
  14. package/dist/get-shader-noise-texture.js.map +7 -0
  15. package/dist/index.d.ts +68 -0
  16. package/dist/index.js +194 -0
  17. package/dist/index.js.map +7 -0
  18. package/dist/shader-color-spaces.d.ts +6 -0
  19. package/dist/shader-color-spaces.js +96 -0
  20. package/dist/shader-color-spaces.js.map +7 -0
  21. package/dist/shader-mount.d.ts +199 -0
  22. package/dist/shader-mount.js +735 -0
  23. package/dist/shader-mount.js.map +7 -0
  24. package/dist/shader-sizing.d.ts +30 -0
  25. package/dist/shader-sizing.js +38 -0
  26. package/dist/shader-sizing.js.map +7 -0
  27. package/dist/shader-utils.d.ts +10 -0
  28. package/dist/shader-utils.js +131 -0
  29. package/dist/shader-utils.js.map +7 -0
  30. package/dist/shaders/color-panels.d.ts +70 -0
  31. package/dist/shaders/color-panels.js +224 -0
  32. package/dist/shaders/color-panels.js.map +7 -0
  33. package/dist/shaders/dithering.d.ts +62 -0
  34. package/dist/shaders/dithering.js +271 -0
  35. package/dist/shaders/dithering.js.map +7 -0
  36. package/dist/shaders/dot-grid.d.ts +65 -0
  37. package/dist/shaders/dot-grid.js +110 -0
  38. package/dist/shaders/dot-grid.js.map +7 -0
  39. package/dist/shaders/dot-orbit.d.ts +57 -0
  40. package/dist/shaders/dot-orbit.js +123 -0
  41. package/dist/shaders/dot-orbit.js.map +7 -0
  42. package/dist/shaders/fluted-glass.d.ts +112 -0
  43. package/dist/shaders/fluted-glass.js +361 -0
  44. package/dist/shaders/fluted-glass.js.map +7 -0
  45. package/dist/shaders/gem-smoke.d.ts +96 -0
  46. package/dist/shaders/gem-smoke.js +573 -0
  47. package/dist/shaders/gem-smoke.js.map +7 -0
  48. package/dist/shaders/god-rays.d.ts +67 -0
  49. package/dist/shaders/god-rays.js +133 -0
  50. package/dist/shaders/god-rays.js.map +7 -0
  51. package/dist/shaders/grain-gradient.d.ts +80 -0
  52. package/dist/shaders/grain-gradient.js +301 -0
  53. package/dist/shaders/grain-gradient.js.map +7 -0
  54. package/dist/shaders/halftone-cmyk.d.ts +104 -0
  55. package/dist/shaders/halftone-cmyk.js +295 -0
  56. package/dist/shaders/halftone-cmyk.js.map +7 -0
  57. package/dist/shaders/halftone-dots.d.ts +80 -0
  58. package/dist/shaders/halftone-dots.js +321 -0
  59. package/dist/shaders/halftone-dots.js.map +7 -0
  60. package/dist/shaders/heatmap.d.ts +66 -0
  61. package/dist/shaders/heatmap.js +384 -0
  62. package/dist/shaders/heatmap.js.map +7 -0
  63. package/dist/shaders/image-dithering.d.ts +64 -0
  64. package/dist/shaders/image-dithering.js +199 -0
  65. package/dist/shaders/image-dithering.js.map +7 -0
  66. package/dist/shaders/lens-distortion.d.ts +97 -0
  67. package/dist/shaders/lens-distortion.js +276 -0
  68. package/dist/shaders/lens-distortion.js.map +7 -0
  69. package/dist/shaders/liquid-metal.d.ts +89 -0
  70. package/dist/shaders/liquid-metal.js +633 -0
  71. package/dist/shaders/liquid-metal.js.map +7 -0
  72. package/dist/shaders/mesh-gradient.d.ts +52 -0
  73. package/dist/shaders/mesh-gradient.js +131 -0
  74. package/dist/shaders/mesh-gradient.js.map +7 -0
  75. package/dist/shaders/metaballs.d.ts +51 -0
  76. package/dist/shaders/metaballs.js +111 -0
  77. package/dist/shaders/metaballs.js.map +7 -0
  78. package/dist/shaders/neuro-noise.d.ts +47 -0
  79. package/dist/shaders/neuro-noise.js +77 -0
  80. package/dist/shaders/neuro-noise.js.map +7 -0
  81. package/dist/shaders/paper-texture.d.ts +109 -0
  82. package/dist/shaders/paper-texture.js +527 -0
  83. package/dist/shaders/paper-texture.js.map +7 -0
  84. package/dist/shaders/perlin-noise.d.ts +52 -0
  85. package/dist/shaders/perlin-noise.js +167 -0
  86. package/dist/shaders/perlin-noise.js.map +7 -0
  87. package/dist/shaders/pulsing-border.d.ts +98 -0
  88. package/dist/shaders/pulsing-border.js +239 -0
  89. package/dist/shaders/pulsing-border.js.map +7 -0
  90. package/dist/shaders/simplex-noise.d.ts +45 -0
  91. package/dist/shaders/simplex-noise.js +92 -0
  92. package/dist/shaders/simplex-noise.js.map +7 -0
  93. package/dist/shaders/smoke-ring.d.ts +61 -0
  94. package/dist/shaders/smoke-ring.js +133 -0
  95. package/dist/shaders/smoke-ring.js.map +7 -0
  96. package/dist/shaders/spiral.d.ts +61 -0
  97. package/dist/shaders/spiral.js +83 -0
  98. package/dist/shaders/spiral.js.map +7 -0
  99. package/dist/shaders/static-mesh-gradient.d.ts +63 -0
  100. package/dist/shaders/static-mesh-gradient.js +131 -0
  101. package/dist/shaders/static-mesh-gradient.js.map +7 -0
  102. package/dist/shaders/static-radial-gradient.d.ts +72 -0
  103. package/dist/shaders/static-radial-gradient.js +188 -0
  104. package/dist/shaders/static-radial-gradient.js.map +7 -0
  105. package/dist/shaders/swirl.d.ts +63 -0
  106. package/dist/shaders/swirl.js +104 -0
  107. package/dist/shaders/swirl.js.map +7 -0
  108. package/dist/shaders/voronoi.d.ts +65 -0
  109. package/dist/shaders/voronoi.js +139 -0
  110. package/dist/shaders/voronoi.js.map +7 -0
  111. package/dist/shaders/warp.d.ts +70 -0
  112. package/dist/shaders/warp.js +130 -0
  113. package/dist/shaders/warp.js.map +7 -0
  114. package/dist/shaders/water.d.ts +59 -0
  115. package/dist/shaders/water.js +122 -0
  116. package/dist/shaders/water.js.map +7 -0
  117. package/dist/shaders/waves.d.ts +53 -0
  118. package/dist/shaders/waves.js +66 -0
  119. package/dist/shaders/waves.js.map +7 -0
  120. package/dist/shared-webgl-renderer.d.ts +63 -0
  121. package/dist/shared-webgl-renderer.js +262 -0
  122. package/dist/shared-webgl-renderer.js.map +7 -0
  123. package/dist/types.d.ts +3 -0
  124. package/dist/types.js +6 -0
  125. package/dist/types.js.map +7 -0
  126. package/dist/vertex-shader.d.ts +2 -0
  127. package/dist/vertex-shader.js +160 -0
  128. package/dist/vertex-shader.js.map +7 -0
  129. package/package.json +29 -0
@@ -0,0 +1,199 @@
1
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * *
2
+ * Paper Shaders *
3
+ * https://github.com/paper-design/shaders *
4
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * */
5
+
6
+ import {} from "../shader-sizing.js";
7
+ import { proceduralHash21, declarePI } from "../shader-utils.js";
8
+ const imageDitheringFragmentShader = `#version 300 es
9
+ precision mediump float;
10
+
11
+ uniform vec2 u_resolution;
12
+ uniform float u_pixelRatio;
13
+ uniform float u_originX;
14
+ uniform float u_originY;
15
+ uniform float u_fit;
16
+
17
+ uniform float u_scale;
18
+ uniform float u_rotation;
19
+ uniform float u_offsetX;
20
+ uniform float u_offsetY;
21
+
22
+ uniform vec4 u_colorFront;
23
+ uniform vec4 u_colorBack;
24
+ uniform vec4 u_colorHighlight;
25
+
26
+ uniform sampler2D u_image;
27
+ uniform float u_imageAspectRatio;
28
+
29
+ uniform float u_type;
30
+ uniform float u_pxSize;
31
+ uniform bool u_originalColors;
32
+ uniform bool u_inverted;
33
+ uniform float u_colorSteps;
34
+
35
+ out vec4 fragColor;
36
+
37
+
38
+ ${proceduralHash21}
39
+ ${declarePI}
40
+
41
+ float getUvFrame(vec2 uv, vec2 pad) {
42
+ float aa = 0.0001;
43
+
44
+ float left = smoothstep(-pad.x, -pad.x + aa, uv.x);
45
+ float right = smoothstep(1.0 + pad.x, 1.0 + pad.x - aa, uv.x);
46
+ float bottom = smoothstep(-pad.y, -pad.y + aa, uv.y);
47
+ float top = smoothstep(1.0 + pad.y, 1.0 + pad.y - aa, uv.y);
48
+
49
+ return left * right * bottom * top;
50
+ }
51
+
52
+ vec2 getImageUV(vec2 uv) {
53
+ vec2 boxOrigin = vec2(.5 - u_originX, u_originY - .5);
54
+ float r = u_rotation * PI / 180.;
55
+ mat2 graphicRotation = mat2(cos(r), sin(r), -sin(r), cos(r));
56
+ vec2 graphicOffset = vec2(-u_offsetX, u_offsetY);
57
+
58
+ vec2 imageBoxSize;
59
+ if (u_fit == 1.) { // contain
60
+ imageBoxSize.x = min(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio;
61
+ } else if (u_fit == 2.) { // cover
62
+ imageBoxSize.x = max(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio;
63
+ } else {
64
+ imageBoxSize.x = min(10.0, 10.0 / u_imageAspectRatio * u_imageAspectRatio);
65
+ }
66
+ imageBoxSize.y = imageBoxSize.x / u_imageAspectRatio;
67
+ vec2 imageBoxScale = u_resolution.xy / imageBoxSize;
68
+
69
+ vec2 imageUV = uv;
70
+ imageUV *= imageBoxScale;
71
+ imageUV += boxOrigin * (imageBoxScale - 1.);
72
+ imageUV += graphicOffset;
73
+ imageUV /= u_scale;
74
+ imageUV.x *= u_imageAspectRatio;
75
+ imageUV = graphicRotation * imageUV;
76
+ imageUV.x /= u_imageAspectRatio;
77
+
78
+ imageUV += .5;
79
+ imageUV.y = 1. - imageUV.y;
80
+
81
+ return imageUV;
82
+ }
83
+
84
+ const int bayer2x2[4] = int[4](0, 2, 3, 1);
85
+ const int bayer4x4[16] = int[16](
86
+ 0, 8, 2, 10,
87
+ 12, 4, 14, 6,
88
+ 3, 11, 1, 9,
89
+ 15, 7, 13, 5
90
+ );
91
+
92
+ const int bayer8x8[64] = int[64](
93
+ 0, 32, 8, 40, 2, 34, 10, 42,
94
+ 48, 16, 56, 24, 50, 18, 58, 26,
95
+ 12, 44, 4, 36, 14, 46, 6, 38,
96
+ 60, 28, 52, 20, 62, 30, 54, 22,
97
+ 3, 35, 11, 43, 1, 33, 9, 41,
98
+ 51, 19, 59, 27, 49, 17, 57, 25,
99
+ 15, 47, 7, 39, 13, 45, 5, 37,
100
+ 63, 31, 55, 23, 61, 29, 53, 21
101
+ );
102
+
103
+ float getBayerValue(vec2 uv, int size) {
104
+ ivec2 pos = ivec2(fract(uv / float(size)) * float(size));
105
+ int index = pos.y * size + pos.x;
106
+
107
+ if (size == 2) {
108
+ return float(bayer2x2[index]) / 4.0;
109
+ } else if (size == 4) {
110
+ return float(bayer4x4[index]) / 16.0;
111
+ } else if (size == 8) {
112
+ return float(bayer8x8[index]) / 64.0;
113
+ }
114
+ return 0.0;
115
+ }
116
+
117
+
118
+ void main() {
119
+
120
+ float pxSize = u_pxSize * u_pixelRatio;
121
+ vec2 pxSizeUV = gl_FragCoord.xy - .5 * u_resolution;
122
+ pxSizeUV /= pxSize;
123
+ vec2 canvasPixelizedUV = (floor(pxSizeUV) + .5) * pxSize;
124
+ vec2 normalizedUV = canvasPixelizedUV / u_resolution;
125
+
126
+ vec2 imageUV = getImageUV(normalizedUV);
127
+ vec2 ditheringNoiseUV = canvasPixelizedUV;
128
+ vec4 image = texture(u_image, imageUV);
129
+ float frame = getUvFrame(imageUV, pxSize / u_resolution);
130
+
131
+ int type = int(floor(u_type));
132
+ float dithering = 0.0;
133
+
134
+ float lum = dot(vec3(.2126, .7152, .0722), image.rgb);
135
+ lum = u_inverted ? (1. - lum) : lum;
136
+
137
+ switch (type) {
138
+ case 1: {
139
+ dithering = step(hash21(ditheringNoiseUV), lum);
140
+ } break;
141
+ case 2:
142
+ dithering = getBayerValue(pxSizeUV, 2);
143
+ break;
144
+ case 3:
145
+ dithering = getBayerValue(pxSizeUV, 4);
146
+ break;
147
+ default :
148
+ dithering = getBayerValue(pxSizeUV, 8);
149
+ break;
150
+ }
151
+
152
+ float colorSteps = max(floor(u_colorSteps), 1.);
153
+ vec3 color = vec3(0.0);
154
+ float opacity = 1.;
155
+
156
+ dithering -= .5;
157
+ float brightness = clamp(lum + dithering / colorSteps, 0.0, 1.0);
158
+ brightness = mix(0.0, brightness, frame);
159
+ brightness = mix(0.0, brightness, image.a);
160
+ float quantLum = floor(brightness * colorSteps + 0.5) / colorSteps;
161
+ quantLum = mix(0.0, quantLum, frame);
162
+
163
+ if (u_originalColors == true) {
164
+ vec3 normColor = image.rgb / max(lum, 0.001);
165
+ color = normColor * quantLum;
166
+
167
+ float quantAlpha = floor(image.a * colorSteps + 0.5) / colorSteps;
168
+ opacity = mix(quantLum, 1., quantAlpha);
169
+ } else {
170
+ vec3 fgColor = u_colorFront.rgb * u_colorFront.a;
171
+ float fgOpacity = u_colorFront.a;
172
+ vec3 bgColor = u_colorBack.rgb * u_colorBack.a;
173
+ float bgOpacity = u_colorBack.a;
174
+ vec3 hlColor = u_colorHighlight.rgb * u_colorHighlight.a;
175
+ float hlOpacity = u_colorHighlight.a;
176
+
177
+ fgColor = mix(fgColor, hlColor, step(1.02 - .02 * u_colorSteps, brightness));
178
+ fgOpacity = mix(fgOpacity, hlOpacity, step(1.02 - .02 * u_colorSteps, brightness));
179
+
180
+ color = fgColor * quantLum;
181
+ opacity = fgOpacity * quantLum;
182
+ color += bgColor * (1.0 - opacity);
183
+ opacity += bgOpacity * (1.0 - opacity);
184
+ }
185
+
186
+ fragColor = vec4(color, opacity);
187
+ }
188
+ `;
189
+ const DitheringTypes = {
190
+ "random": 1,
191
+ "2x2": 2,
192
+ "4x4": 3,
193
+ "8x8": 4
194
+ };
195
+ export {
196
+ DitheringTypes,
197
+ imageDitheringFragmentShader
198
+ };
199
+ //# 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';\r\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\r\nimport { proceduralHash21, declarePI } from '../shader-utils.js';\r\n\r\n/**\r\n * A dithering image filter with support for 4 dithering modes and multiple color palettes\r\n * (2-color, 3-color, and multicolor options, using either predefined colors or colors sampled\r\n * from the original image).\r\n *\r\n * SIZING NOTE: This shader performs sizing in the fragment shader (not vertex shader) to keep\r\n * u_pxSize in consistent actual pixels. The pixel grid is computed from gl_FragCoord before any\r\n * transforms, so scaling/rotating only affects the underlying image.\r\n * No vertex shader outputs (v_imageUV, v_objectUV, etc.) are used.\r\n *\r\n * Fragment shader uniforms:\r\n * - u_resolution (vec2): Canvas resolution in pixels\r\n * - u_pixelRatio (float): Device pixel ratio\r\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\r\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\r\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\r\n * - u_scale (float): Overall zoom level of the graphics (0.1 to 4)\r\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\r\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\r\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\r\n * - u_image (sampler2D): Source image texture\r\n * - u_imageAspectRatio (float): Aspect ratio of the source image\r\n * - u_colorFront (vec4): Foreground color in RGBA, needs originalColors off\r\n * - u_colorBack (vec4): Background color in RGBA, needs originalColors off\r\n * - u_colorHighlight (vec4): Secondary foreground color in RGBA (set same as colorFront for classic 2-color dithering), needs originalColors off\r\n * - u_originalColors (bool): Use the original colors of the image instead of the color palette\r\n * - u_inverted (bool): Inverts the image luminance, doesn't affect the color scheme\r\n * - u_type (float): Dithering type (1 = random, 2 = 2x2 Bayer, 3 = 4x4 Bayer, 4 = 8x8 Bayer)\r\n * - u_pxSize (float): Pixel size of dithering grid (0.5 to 20)\r\n * - u_colorSteps (float): Number of colors to use, applies to both color modes (1 to 7)\r\n *\r\n */\r\n\r\n// language=GLSL\r\nexport const imageDitheringFragmentShader: string = `#version 300 es\r\nprecision mediump float;\r\n\r\nuniform vec2 u_resolution;\r\nuniform float u_pixelRatio;\r\nuniform float u_originX;\r\nuniform float u_originY;\r\nuniform float u_fit;\r\n\r\nuniform float u_scale;\r\nuniform float u_rotation;\r\nuniform float u_offsetX;\r\nuniform float u_offsetY;\r\n\r\nuniform vec4 u_colorFront;\r\nuniform vec4 u_colorBack;\r\nuniform vec4 u_colorHighlight;\r\n\r\nuniform sampler2D u_image;\r\nuniform float u_imageAspectRatio;\r\n\r\nuniform float u_type;\r\nuniform float u_pxSize;\r\nuniform bool u_originalColors;\r\nuniform bool u_inverted;\r\nuniform float u_colorSteps;\r\n\r\nout vec4 fragColor;\r\n\r\n\r\n${ proceduralHash21 }\r\n${ declarePI }\r\n\r\nfloat getUvFrame(vec2 uv, vec2 pad) {\r\n float aa = 0.0001;\r\n\r\n float left = smoothstep(-pad.x, -pad.x + aa, uv.x);\r\n float right = smoothstep(1.0 + pad.x, 1.0 + pad.x - aa, uv.x);\r\n float bottom = smoothstep(-pad.y, -pad.y + aa, uv.y);\r\n float top = smoothstep(1.0 + pad.y, 1.0 + pad.y - aa, uv.y);\r\n\r\n return left * right * bottom * top;\r\n}\r\n\r\nvec2 getImageUV(vec2 uv) {\r\n vec2 boxOrigin = vec2(.5 - u_originX, u_originY - .5);\r\n float r = u_rotation * PI / 180.;\r\n mat2 graphicRotation = mat2(cos(r), sin(r), -sin(r), cos(r));\r\n vec2 graphicOffset = vec2(-u_offsetX, u_offsetY);\r\n\r\n vec2 imageBoxSize;\r\n if (u_fit == 1.) { // contain\r\n imageBoxSize.x = min(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio;\r\n } else if (u_fit == 2.) { // cover\r\n imageBoxSize.x = max(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio;\r\n } else {\r\n imageBoxSize.x = min(10.0, 10.0 / u_imageAspectRatio * u_imageAspectRatio);\r\n }\r\n imageBoxSize.y = imageBoxSize.x / u_imageAspectRatio;\r\n vec2 imageBoxScale = u_resolution.xy / imageBoxSize;\r\n\r\n vec2 imageUV = uv;\r\n imageUV *= imageBoxScale;\r\n imageUV += boxOrigin * (imageBoxScale - 1.);\r\n imageUV += graphicOffset;\r\n imageUV /= u_scale;\r\n imageUV.x *= u_imageAspectRatio;\r\n imageUV = graphicRotation * imageUV;\r\n imageUV.x /= u_imageAspectRatio;\r\n\r\n imageUV += .5;\r\n imageUV.y = 1. - imageUV.y;\r\n\r\n return imageUV;\r\n}\r\n\r\nconst int bayer2x2[4] = int[4](0, 2, 3, 1);\r\nconst int bayer4x4[16] = int[16](\r\n0, 8, 2, 10,\r\n12, 4, 14, 6,\r\n3, 11, 1, 9,\r\n15, 7, 13, 5\r\n);\r\n\r\nconst int bayer8x8[64] = int[64](\r\n0, 32, 8, 40, 2, 34, 10, 42,\r\n48, 16, 56, 24, 50, 18, 58, 26,\r\n12, 44, 4, 36, 14, 46, 6, 38,\r\n60, 28, 52, 20, 62, 30, 54, 22,\r\n3, 35, 11, 43, 1, 33, 9, 41,\r\n51, 19, 59, 27, 49, 17, 57, 25,\r\n15, 47, 7, 39, 13, 45, 5, 37,\r\n63, 31, 55, 23, 61, 29, 53, 21\r\n);\r\n\r\nfloat getBayerValue(vec2 uv, int size) {\r\n ivec2 pos = ivec2(fract(uv / float(size)) * float(size));\r\n int index = pos.y * size + pos.x;\r\n\r\n if (size == 2) {\r\n return float(bayer2x2[index]) / 4.0;\r\n } else if (size == 4) {\r\n return float(bayer4x4[index]) / 16.0;\r\n } else if (size == 8) {\r\n return float(bayer8x8[index]) / 64.0;\r\n }\r\n return 0.0;\r\n}\r\n\r\n\r\nvoid main() {\r\n\r\n float pxSize = u_pxSize * u_pixelRatio;\r\n vec2 pxSizeUV = gl_FragCoord.xy - .5 * u_resolution;\r\n pxSizeUV /= pxSize;\r\n vec2 canvasPixelizedUV = (floor(pxSizeUV) + .5) * pxSize;\r\n vec2 normalizedUV = canvasPixelizedUV / u_resolution;\r\n\r\n vec2 imageUV = getImageUV(normalizedUV);\r\n vec2 ditheringNoiseUV = canvasPixelizedUV;\r\n vec4 image = texture(u_image, imageUV);\r\n float frame = getUvFrame(imageUV, pxSize / u_resolution);\r\n\r\n int type = int(floor(u_type));\r\n float dithering = 0.0;\r\n\r\n float lum = dot(vec3(.2126, .7152, .0722), image.rgb);\r\n lum = u_inverted ? (1. - lum) : lum;\r\n\r\n switch (type) {\r\n case 1: {\r\n dithering = step(hash21(ditheringNoiseUV), lum);\r\n } break;\r\n case 2:\r\n dithering = getBayerValue(pxSizeUV, 2);\r\n break;\r\n case 3:\r\n dithering = getBayerValue(pxSizeUV, 4);\r\n break;\r\n default :\r\n dithering = getBayerValue(pxSizeUV, 8);\r\n break;\r\n }\r\n\r\n float colorSteps = max(floor(u_colorSteps), 1.);\r\n vec3 color = vec3(0.0);\r\n float opacity = 1.;\r\n\r\n dithering -= .5;\r\n float brightness = clamp(lum + dithering / colorSteps, 0.0, 1.0);\r\n brightness = mix(0.0, brightness, frame);\r\n brightness = mix(0.0, brightness, image.a);\r\n float quantLum = floor(brightness * colorSteps + 0.5) / colorSteps;\r\n quantLum = mix(0.0, quantLum, frame);\r\n\r\n if (u_originalColors == true) {\r\n vec3 normColor = image.rgb / max(lum, 0.001);\r\n color = normColor * quantLum;\r\n\r\n float quantAlpha = floor(image.a * colorSteps + 0.5) / colorSteps;\r\n opacity = mix(quantLum, 1., quantAlpha);\r\n } else {\r\n vec3 fgColor = u_colorFront.rgb * u_colorFront.a;\r\n float fgOpacity = u_colorFront.a;\r\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\r\n float bgOpacity = u_colorBack.a;\r\n vec3 hlColor = u_colorHighlight.rgb * u_colorHighlight.a;\r\n float hlOpacity = u_colorHighlight.a;\r\n\r\n fgColor = mix(fgColor, hlColor, step(1.02 - .02 * u_colorSteps, brightness));\r\n fgOpacity = mix(fgOpacity, hlOpacity, step(1.02 - .02 * u_colorSteps, brightness));\r\n\r\n color = fgColor * quantLum;\r\n opacity = fgOpacity * quantLum;\r\n color += bgColor * (1.0 - opacity);\r\n opacity += bgOpacity * (1.0 - opacity);\r\n }\r\n\r\n fragColor = vec4(color, opacity);\r\n}\r\n`;\r\n\r\nexport interface ImageDitheringUniforms extends ShaderSizingUniforms {\r\n u_image: HTMLImageElement | string;\r\n u_colorFront: [number, number, number, number];\r\n u_colorBack: [number, number, number, number];\r\n u_colorHighlight: [number, number, number, number];\r\n u_type: (typeof DitheringTypes)[DitheringType];\r\n u_pxSize: number;\r\n u_colorSteps: number;\r\n u_originalColors: boolean;\r\n u_inverted: boolean;\r\n}\r\n\r\nexport interface ImageDitheringParams extends ShaderSizingParams, ShaderMotionParams {\r\n image: HTMLImageElement | string;\r\n colorFront?: string;\r\n colorBack?: string;\r\n colorHighlight?: string;\r\n type?: DitheringType;\r\n size?: number;\r\n colorSteps?: number;\r\n originalColors?: boolean;\r\n inverted?: boolean;\r\n}\r\n\r\nexport const DitheringTypes = {\r\n 'random': 1,\r\n '2x2': 2,\r\n '4x4': 3,\r\n '8x8': 4,\r\n} as const;\r\n\r\nexport type DitheringType = keyof typeof DitheringTypes;\r\n"],
5
+ "mappings": ";;;;;AACA,eAAmE;AACnE,SAAS,kBAAkB,iBAAiB;AAoCrC,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,EA8BjD,gBAAiB;AAAA,EACjB,SAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA+KN,MAAM,iBAAiB;AAAA,EAC5B,UAAU;AAAA,EACV,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,97 @@
1
+ import type { ShaderMotionParams } from '../shader-mount.js';
2
+ import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';
3
+ export declare const lensDistortionMeta: {
4
+ readonly maxSamples: 50;
5
+ };
6
+ /**
7
+ *
8
+ * Lens Distortion image filter separates an image into shifting color layers
9
+ * (recreating the chromatic aberration of a lens) and warps the image geometry,
10
+ * curving it outward or inward like barrel and pincushion distortion.
11
+ *
12
+ * Fragment shader uniforms:
13
+ * - u_image (sampler2D): Source image texture
14
+ * - u_spread (float): Strength of the color split; how far the color layers are pushed apart; 0 is off (0 to 1)
15
+ * - u_bias (float): Shifts the colors toward one end of the spread; 0 spaces them evenly, needs spread > 0, count > 2 (-1 to 1)
16
+ * - u_angle (float): Direction of the spread in degrees, needs spread > 0, perspective < 1 (0 to 360)
17
+ * - u_perspective (float): Shapes the spread direction from a straight line (0) to a radial burst out from the centre (1), needs spread > 0 (0 to 1)
18
+ * - u_count (float): Number of sampled color layers along the spread; higher is smoother and costlier, needs spread > 0 (2 to 50)
19
+ * - u_dispersion (float): Overall amount of color dispersion; 1 gives each layer its own color from the spectrum, 0 keeps the original image color, needs spread > 0 (0 to 1)
20
+ * - u_dispersionShift (float): Balance of the dispersion between a soft circular zone at the centre and the rest of the image; 0 applies it evenly, -1 keeps it in the centre only, 1 keeps it at the edges only, needs spread > 0, dispersion > 0 (-1 to 1)
21
+ * - u_dispersionColor (float): Rotates the colors around the hue wheel, 0 to 1 for a full turn, needs spread > 0, dispersion > 0
22
+ * - u_focusCenter (float): Reduces the spread in a circular zone at the centre; 0 keeps it full to the centre, needs spread > 0 (0 to 1)
23
+ * - u_focusEdges (float): Reduces the spread toward the edges; 0 keeps it full to the edges, 1 restores the original image there, needs spread > 0 (0 to 1)
24
+ * - u_swirl (float): Rotates the color layers around the centre by an angle growing along the spread; 0 is off, needs spread > 0 (-1 to 1)
25
+ * - u_noise (float): Scatters the spread direction with noise; 0 is off, needs spread > 0 (0 to 1)
26
+ * - u_noiseFrequency (float): Frequency of the noise, 0 to 1 mapped internally to 0 to 18; higher is finer, needs spread > 0, noise > 0
27
+ * - u_noiseOffset (float): Offsets the noise pattern for a different seed, 0 to 1 mapped internally to 0 to 30, needs spread > 0, noise > 0
28
+ * - u_lensBulge (float): Radial lens warp of the image geometry; positive bulges out like a fisheye/barrel, negative pinches in like a pincushion; strong positive values fade out the corners (-1 to 1)
29
+ * - u_lensCircle (float): Squeezes pixels outside the inscribed circle inward so the outline becomes a circle; also turns the spread radial and damps it near the rim; 0 is off, 1 is full (0 to 1)
30
+ * - u_grainMixer (float): Scatters the spread with grain noise, breaking up the edges of the color layers, needs spread > 0 (0 to 1)
31
+ * - u_grainOverlay (float): Post-processing black/white grain overlay (0 to 1)
32
+ * - u_imageX (float): Pans the image horizontally behind the effect; 0 is centred (-1 to 1)
33
+ * - u_imageY (float): Pans the image vertically behind the effect; 0 is centred (-1 to 1)
34
+ *
35
+ * Vertex shader outputs (used in fragment shader):
36
+ * - v_imageUV (vec2): Image UV coordinates with global sizing (rotation, scale, offset, etc) applied
37
+ *
38
+ * Vertex shader uniforms:
39
+ * - u_resolution (vec2): Canvas resolution in pixels
40
+ * - u_pixelRatio (float): Device pixel ratio
41
+ * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)
42
+ * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)
43
+ * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)
44
+ * - u_scale (float): Overall zoom level of the graphics (0.1 to 4)
45
+ * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)
46
+ * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)
47
+ * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)
48
+ * - u_imageAspectRatio (float): Aspect ratio of the source image
49
+ *
50
+ */
51
+ export declare const lensDistortionFragmentShader: string;
52
+ export interface LensDistortionUniforms extends ShaderSizingUniforms {
53
+ u_image: HTMLImageElement | string | undefined;
54
+ u_spread: number;
55
+ u_bias: number;
56
+ u_angle: number;
57
+ u_perspective: number;
58
+ u_count: number;
59
+ u_dispersion: number;
60
+ u_dispersionShift: number;
61
+ u_dispersionColor: number;
62
+ u_focusCenter: number;
63
+ u_focusEdges: number;
64
+ u_swirl: number;
65
+ u_noise: number;
66
+ u_noiseFrequency: number;
67
+ u_noiseOffset: number;
68
+ u_lensBulge: number;
69
+ u_lensCircle: number;
70
+ u_grainMixer: number;
71
+ u_grainOverlay: number;
72
+ u_imageX: number;
73
+ u_imageY: number;
74
+ }
75
+ export interface LensDistortionParams extends ShaderSizingParams, ShaderMotionParams {
76
+ image?: HTMLImageElement | string;
77
+ spread?: number;
78
+ bias?: number;
79
+ angle?: number;
80
+ perspective?: number;
81
+ count?: number;
82
+ dispersion?: number;
83
+ dispersionShift?: number;
84
+ dispersionColor?: number;
85
+ focusCenter?: number;
86
+ focusEdges?: number;
87
+ swirl?: number;
88
+ noise?: number;
89
+ noiseFrequency?: number;
90
+ noiseOffset?: number;
91
+ lensBulge?: number;
92
+ lensCircle?: number;
93
+ grainMixer?: number;
94
+ grainOverlay?: number;
95
+ imageX?: number;
96
+ imageY?: number;
97
+ }
@@ -0,0 +1,276 @@
1
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * *
2
+ * Paper Shaders *
3
+ * https://github.com/paper-design/shaders *
4
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * */
5
+
6
+ import {} from "../shader-sizing.js";
7
+ import { declarePI, proceduralHash21, rotation2 } from "../shader-utils.js";
8
+ const lensDistortionMeta = {
9
+ maxSamples: 50
10
+ };
11
+ const lensDistortionFragmentShader = `#version 300 es
12
+ precision mediump float;
13
+
14
+ uniform sampler2D u_image;
15
+ uniform float u_imageAspectRatio;
16
+ uniform float u_spread;
17
+ uniform float u_bias;
18
+ uniform float u_angle;
19
+ uniform float u_perspective;
20
+ uniform float u_count;
21
+ uniform float u_dispersion;
22
+ uniform float u_dispersionShift;
23
+ uniform float u_dispersionColor;
24
+ uniform float u_focusCenter;
25
+ uniform float u_focusEdges;
26
+ uniform float u_swirl;
27
+ uniform float u_noise;
28
+ uniform float u_noiseFrequency;
29
+ uniform float u_noiseOffset;
30
+ uniform float u_lensBulge;
31
+ uniform float u_lensCircle;
32
+ uniform float u_grainMixer;
33
+ uniform float u_grainOverlay;
34
+ uniform float u_imageX;
35
+ uniform float u_imageY;
36
+
37
+ in vec2 v_imageUV;
38
+
39
+ out vec4 fragColor;
40
+
41
+ ${declarePI}
42
+ ${proceduralHash21}
43
+ ${rotation2}
44
+
45
+ float valueNoise(vec2 st) {
46
+ vec2 i = floor(st);
47
+ vec2 f = fract(st);
48
+ float a = hash21(i);
49
+ float b = hash21(i + vec2(1., 0.));
50
+ float c = hash21(i + vec2(0., 1.));
51
+ float d = hash21(i + vec2(1., 1.));
52
+ vec2 u = f * f * (3. - 2. * f);
53
+ return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
54
+ }
55
+
56
+ float getUvFrame(vec2 uv) {
57
+ vec2 invAA = 1. / clamp(fwidth(uv), 1e-5, .02);
58
+ vec2 lo = clamp(uv * invAA + .5, 0., 1.);
59
+ vec2 hi = clamp((1. - uv) * invAA + .5, 0., 1.);
60
+ return lo.x * hi.x * lo.y * hi.y;
61
+ }
62
+
63
+ vec4 sampleOverWhite(vec2 uv) {
64
+ vec4 img = texture(u_image, uv);
65
+ float cover = img.a * getUvFrame(uv);
66
+ vec3 colorOverWhite = mix(vec3(1.), img.rgb, cover);
67
+ return vec4(colorOverWhite, cover);
68
+ }
69
+
70
+ vec3 hueColor(float hue) {
71
+ vec3 rgb = clamp(abs(mod(hue * 6. + vec3(0., 4., 2.), 6.) - 3.) - 1., 0., 1.);
72
+ rgb = rgb * rgb * (3. - 2. * rgb);
73
+ return rgb;
74
+ }
75
+
76
+ float boxInradius() {
77
+ return .5 * min(u_imageAspectRatio, 1.);
78
+ }
79
+
80
+ float boxOutradius() {
81
+ return .5 * length(vec2(u_imageAspectRatio, 1.));
82
+ }
83
+
84
+ float spreadReach() {
85
+ return .7 * pow(u_spread, 1.3 + 2.7 * u_spread);
86
+ }
87
+
88
+ float innerCircleMask(float radius, float inradius) {
89
+ return 1. - smoothstep(.5 * inradius, 1.1 * inradius, radius);
90
+ }
91
+
92
+ float dispersionCurve(float t, float biasPow) {
93
+ float mirroredT = u_bias < 0. ? 1. - t : t;
94
+ float curved = pow(mirroredT, biasPow);
95
+ return u_bias < 0. ? 1. - curved : curved;
96
+ }
97
+
98
+ vec2 getSpread(vec2 fromCenter, float radius, vec2 warpedUV, float edgeAA, float inradius, float outradius, float reach, out float outStrength) {
99
+ float angleRad = radians(u_angle);
100
+ vec2 uniformDir = vec2(cos(angleRad), sin(angleRad));
101
+
102
+ float invInradius = 1. / inradius;
103
+ vec2 radialDir = fromCenter * invInradius;
104
+ float maxLen = (outradius + reach) * invInradius;
105
+ float radialLen = radius * invInradius;
106
+ if (radialLen > maxLen) radialDir *= maxLen / radialLen;
107
+ vec2 spreadDir = mix(uniformDir, radialDir, u_perspective);
108
+
109
+ float bandProximity = smoothstep(inradius * .8, inradius, radius);
110
+ float lensCircleMaxing = bandProximity * u_lensCircle * u_lensCircle * u_lensCircle;
111
+ spreadDir = mix(spreadDir, radialDir, lensCircleMaxing);
112
+
113
+ vec2 warpedAbs = abs(warpedUV - .5);
114
+ float inner = mix(1., smoothstep(0., mix(inradius, outradius, u_focusCenter), radius), u_focusCenter);
115
+ float boxDist = max(warpedAbs.x, warpedAbs.y) * 2.;
116
+ float outer = mix(1., 1. - min(boxDist, 1.), u_focusEdges);
117
+ float strength = inner * outer;
118
+
119
+ strength *= mix(1., mix(.15, .03, max(-u_lensBulge, 0.)), lensCircleMaxing);
120
+
121
+ vec2 outside = max(warpedAbs - .5, 0.);
122
+ float margin = max(reach * length(spreadDir) * (1. - u_lensCircle), edgeAA);
123
+ strength *= 1. - smoothstep(0., margin, length(outside));
124
+
125
+ outStrength = strength;
126
+ vec2 axis = spreadDir * (reach * strength);
127
+
128
+ if (u_noise > 0.) {
129
+ float turn = (valueNoise(fromCenter * u_noiseFrequency * 18. + u_noiseOffset * 30.) - .5) * 2. * u_noise;
130
+ float cs = cos(turn), sn = sin(turn);
131
+ axis = mat2(cs, -sn, sn, cs) * axis;
132
+ }
133
+
134
+ axis.x /= u_imageAspectRatio;
135
+ return axis;
136
+ }
137
+
138
+ vec2 lensWarp(vec2 fromCenter, float radius, float inradius, out float bulgeFade) {
139
+ bulgeFade = 1.;
140
+ if (u_lensBulge == 0. && u_lensCircle <= 0.) return fromCenter;
141
+ if (radius < 1e-5) return fromCenter;
142
+
143
+ float r = radius;
144
+
145
+ if (u_lensBulge != 0.) {
146
+ float rn = radius / inradius;
147
+ float bulge = abs(u_lensBulge) * (u_lensBulge > 0. ? 1.4 : 1.2);
148
+ if (u_lensBulge > 0.) bulgeFade = 1. - smoothstep(1.45, 1.53, rn * bulge);
149
+ float map = u_lensBulge > 0.
150
+ ? tan(min(rn * bulge, 1.53)) / tan(bulge)
151
+ : atan(rn * tan(bulge)) / bulge;
152
+ float bulgeScale = map / rn;
153
+ fromCenter *= bulgeScale;
154
+ r *= bulgeScale;
155
+ }
156
+
157
+ if (u_lensCircle > 0.) {
158
+ vec2 dir = fromCenter / max(r, 1e-5);
159
+ vec2 halfBox = vec2(u_imageAspectRatio, 1.) * .5;
160
+ float rBox = min(halfBox.x / max(abs(dir.x), 1e-4), halfBox.y / max(abs(dir.y), 1e-4));
161
+ float band = inradius * mix(.03, .2, .5 * (u_lensBulge + 1.));
162
+ float innerEdge = inradius - band;
163
+ float over = smoothstep(0., 1., (r - innerEdge) / band);
164
+ float g = r + (rBox - inradius) * pow(over, 14.);
165
+ fromCenter = dir * mix(r, g, u_lensCircle);
166
+ }
167
+
168
+ return fromCenter;
169
+ }
170
+
171
+ void main() {
172
+ vec2 uv = v_imageUV;
173
+
174
+ float invAspect = 1. / u_imageAspectRatio;
175
+ float inradius = boxInradius();
176
+ float outradius = boxOutradius();
177
+ float reach = spreadReach();
178
+
179
+ vec2 fromCenter = uv - .5;
180
+ fromCenter.x *= u_imageAspectRatio;
181
+ float radius = length(fromCenter);
182
+
183
+ float bulgeFade;
184
+ vec2 warpedFromCenter = lensWarp(fromCenter, radius, inradius, bulgeFade);
185
+ vec2 baseUV = vec2(warpedFromCenter.x * invAspect, warpedFromCenter.y) + .5;
186
+
187
+ vec2 baseDerivative = fwidth(baseUV);
188
+ float edgeAA = clamp(2. * max(baseDerivative.x, baseDerivative.y), .001, .02);
189
+
190
+ float spreadStrength;
191
+ vec2 spreadAxis = getSpread(fromCenter, radius, baseUV, edgeAA, inradius, outradius, reach, spreadStrength);
192
+
193
+ vec2 grainUV = vec2(0.);
194
+ if (u_grainMixer > 0. || u_grainOverlay > 0.) {
195
+ vec2 dudx = dFdx(v_imageUV);
196
+ vec2 dudy = dFdy(v_imageUV);
197
+ grainUV = (v_imageUV - .5) * (.8 / vec2(length(dudx), length(dudy))) + .5;
198
+ }
199
+
200
+ if (u_grainMixer > 0.) {
201
+ float grainSeed = valueNoise(grainUV);
202
+ vec2 jit = fract(grainSeed * vec2(157.31, 113.57)) * 2. - 1.;
203
+ spreadAxis += jit * .3 * u_grainMixer * length(spreadAxis);
204
+ }
205
+
206
+ float count = floor(u_count);
207
+ int countInteger = int(count);
208
+
209
+ float invCount = 1. / count;
210
+ float invSpan = 1. / max(count - 1., 1.);
211
+ float hueBase = u_dispersionColor + .5;
212
+ float biasPower = 1. + 2. * abs(u_bias);
213
+ vec2 tapBias = .5 - vec2(u_imageX, u_imageY);
214
+
215
+ float centerAmount = clamp(1. - u_dispersionShift, 0., 1.);
216
+ float edgesAmount = clamp(1. + u_dispersionShift, 0., 1.);
217
+ float dispersion = u_dispersion * mix(edgesAmount, centerAmount, innerCircleMask(radius, inradius));
218
+ float dispersionPower = pow(dispersion, .8);
219
+
220
+ float warpedRadius = length(warpedFromCenter);
221
+ float swirlAngle = u_swirl * .4 * PI * spreadStrength * u_spread * min(1., inradius / max(warpedRadius, 1e-4));
222
+
223
+ vec3 colorSum = vec3(0.);
224
+ vec3 weightSum = vec3(0.);
225
+ float coverSum = 0.;
226
+
227
+ for (int i = 0; i < ${lensDistortionMeta.maxSamples}; i++) {
228
+ if (i >= countInteger) break;
229
+
230
+ float layer = float(i);
231
+ float hue = hueBase + layer * invCount;
232
+
233
+ float spread = layer * invSpan;
234
+ if (u_bias != 0.) spread = dispersionCurve(spread, biasPower);
235
+
236
+ // mix(axis, -axis, spread) is axis scaled by the signed position along the fan
237
+ float fanPos = 1. - 2. * spread;
238
+ vec2 tapUV = baseUV + spreadAxis * fanPos - .5;
239
+
240
+ if (u_swirl != 0.) {
241
+ tapUV.x *= u_imageAspectRatio;
242
+ tapUV = rotate(tapUV, swirlAngle * fanPos);
243
+ tapUV.x *= invAspect;
244
+ }
245
+
246
+ vec4 tap = sampleOverWhite(tapUV + tapBias);
247
+ vec3 weight = 1. - dispersionPower * hueColor(hue);
248
+
249
+ colorSum += tap.rgb * weight;
250
+ weightSum += weight;
251
+ coverSum += tap.a;
252
+ }
253
+
254
+ vec3 color = colorSum / max(weightSum, 1e-4);
255
+ float coverAvg = coverSum * invCount;
256
+
257
+ float ground = min(color.r, min(color.g, color.b));
258
+ float alpha = max(coverAvg, 1. - ground);
259
+ vec3 premult = max(color - (1. - alpha), 0.);
260
+ fragColor = vec4(premult, alpha) * bulgeFade;
261
+
262
+ if (u_grainOverlay > 0.) {
263
+ float grain = valueNoise(rotate(grainUV, 1.) + vec2(3.));
264
+ grain = mix(grain, valueNoise(rotate(grainUV, 2.) + vec2(-1.)), .5);
265
+ grain = pow(grain, 1.3);
266
+ float grainV = grain * 2. - 1.;
267
+ float grainStrength = pow(u_grainOverlay * abs(grainV), .8) * fragColor.a;
268
+ fragColor.rgb = mix(fragColor.rgb, vec3(step(0., grainV)) * fragColor.a, .35 * grainStrength);
269
+ }
270
+ }
271
+ `;
272
+ export {
273
+ lensDistortionFragmentShader,
274
+ lensDistortionMeta
275
+ };
276
+ //# sourceMappingURL=lens-distortion.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/shaders/lens-distortion.ts"],
4
+ "sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\r\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\r\nimport { declarePI, proceduralHash21, rotation2 } from '../shader-utils.js';\r\n\r\nexport const lensDistortionMeta = {\r\n maxSamples: 50,\r\n} as const;\r\n\r\n/**\r\n *\r\n * Lens Distortion image filter separates an image into shifting color layers\r\n * (recreating the chromatic aberration of a lens) and warps the image geometry,\r\n * curving it outward or inward like barrel and pincushion distortion.\r\n *\r\n * Fragment shader uniforms:\r\n * - u_image (sampler2D): Source image texture\r\n * - u_spread (float): Strength of the color split; how far the color layers are pushed apart; 0 is off (0 to 1)\r\n * - u_bias (float): Shifts the colors toward one end of the spread; 0 spaces them evenly, needs spread > 0, count > 2 (-1 to 1)\r\n * - u_angle (float): Direction of the spread in degrees, needs spread > 0, perspective < 1 (0 to 360)\r\n * - u_perspective (float): Shapes the spread direction from a straight line (0) to a radial burst out from the centre (1), needs spread > 0 (0 to 1)\r\n * - u_count (float): Number of sampled color layers along the spread; higher is smoother and costlier, needs spread > 0 (2 to 50)\r\n * - u_dispersion (float): Overall amount of color dispersion; 1 gives each layer its own color from the spectrum, 0 keeps the original image color, needs spread > 0 (0 to 1)\r\n * - u_dispersionShift (float): Balance of the dispersion between a soft circular zone at the centre and the rest of the image; 0 applies it evenly, -1 keeps it in the centre only, 1 keeps it at the edges only, needs spread > 0, dispersion > 0 (-1 to 1)\r\n * - u_dispersionColor (float): Rotates the colors around the hue wheel, 0 to 1 for a full turn, needs spread > 0, dispersion > 0\r\n * - u_focusCenter (float): Reduces the spread in a circular zone at the centre; 0 keeps it full to the centre, needs spread > 0 (0 to 1)\r\n * - u_focusEdges (float): Reduces the spread toward the edges; 0 keeps it full to the edges, 1 restores the original image there, needs spread > 0 (0 to 1)\r\n * - u_swirl (float): Rotates the color layers around the centre by an angle growing along the spread; 0 is off, needs spread > 0 (-1 to 1)\r\n * - u_noise (float): Scatters the spread direction with noise; 0 is off, needs spread > 0 (0 to 1)\r\n * - u_noiseFrequency (float): Frequency of the noise, 0 to 1 mapped internally to 0 to 18; higher is finer, needs spread > 0, noise > 0\r\n * - u_noiseOffset (float): Offsets the noise pattern for a different seed, 0 to 1 mapped internally to 0 to 30, needs spread > 0, noise > 0\r\n * - u_lensBulge (float): Radial lens warp of the image geometry; positive bulges out like a fisheye/barrel, negative pinches in like a pincushion; strong positive values fade out the corners (-1 to 1)\r\n * - u_lensCircle (float): Squeezes pixels outside the inscribed circle inward so the outline becomes a circle; also turns the spread radial and damps it near the rim; 0 is off, 1 is full (0 to 1)\r\n * - u_grainMixer (float): Scatters the spread with grain noise, breaking up the edges of the color layers, needs spread > 0 (0 to 1)\r\n * - u_grainOverlay (float): Post-processing black/white grain overlay (0 to 1)\r\n * - u_imageX (float): Pans the image horizontally behind the effect; 0 is centred (-1 to 1)\r\n * - u_imageY (float): Pans the image vertically behind the effect; 0 is centred (-1 to 1)\r\n *\r\n * Vertex shader outputs (used in fragment shader):\r\n * - v_imageUV (vec2): Image UV coordinates with global sizing (rotation, scale, offset, etc) applied\r\n *\r\n * Vertex shader uniforms:\r\n * - u_resolution (vec2): Canvas resolution in pixels\r\n * - u_pixelRatio (float): Device pixel ratio\r\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\r\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\r\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\r\n * - u_scale (float): Overall zoom level of the graphics (0.1 to 4)\r\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\r\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\r\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\r\n * - u_imageAspectRatio (float): Aspect ratio of the source image\r\n *\r\n */\r\n\r\n// language=GLSL\r\nexport const lensDistortionFragmentShader: string = `#version 300 es\r\nprecision mediump float;\r\n\r\nuniform sampler2D u_image;\r\nuniform float u_imageAspectRatio;\r\nuniform float u_spread;\r\nuniform float u_bias;\r\nuniform float u_angle;\r\nuniform float u_perspective;\r\nuniform float u_count;\r\nuniform float u_dispersion;\r\nuniform float u_dispersionShift;\r\nuniform float u_dispersionColor;\r\nuniform float u_focusCenter;\r\nuniform float u_focusEdges;\r\nuniform float u_swirl;\r\nuniform float u_noise;\r\nuniform float u_noiseFrequency;\r\nuniform float u_noiseOffset;\r\nuniform float u_lensBulge;\r\nuniform float u_lensCircle;\r\nuniform float u_grainMixer;\r\nuniform float u_grainOverlay;\r\nuniform float u_imageX;\r\nuniform float u_imageY;\r\n\r\nin vec2 v_imageUV;\r\n\r\nout vec4 fragColor;\r\n\r\n${declarePI}\r\n${proceduralHash21}\r\n${rotation2}\r\n\r\nfloat valueNoise(vec2 st) {\r\n vec2 i = floor(st);\r\n vec2 f = fract(st);\r\n float a = hash21(i);\r\n float b = hash21(i + vec2(1., 0.));\r\n float c = hash21(i + vec2(0., 1.));\r\n float d = hash21(i + vec2(1., 1.));\r\n vec2 u = f * f * (3. - 2. * f);\r\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\r\n}\r\n\r\nfloat getUvFrame(vec2 uv) {\r\n vec2 invAA = 1. / clamp(fwidth(uv), 1e-5, .02);\r\n vec2 lo = clamp(uv * invAA + .5, 0., 1.);\r\n vec2 hi = clamp((1. - uv) * invAA + .5, 0., 1.);\r\n return lo.x * hi.x * lo.y * hi.y;\r\n}\r\n\r\nvec4 sampleOverWhite(vec2 uv) {\r\n vec4 img = texture(u_image, uv);\r\n float cover = img.a * getUvFrame(uv);\r\n vec3 colorOverWhite = mix(vec3(1.), img.rgb, cover);\r\n return vec4(colorOverWhite, cover);\r\n}\r\n\r\nvec3 hueColor(float hue) {\r\n vec3 rgb = clamp(abs(mod(hue * 6. + vec3(0., 4., 2.), 6.) - 3.) - 1., 0., 1.);\r\n rgb = rgb * rgb * (3. - 2. * rgb);\r\n return rgb;\r\n}\r\n \r\nfloat boxInradius() {\r\n return .5 * min(u_imageAspectRatio, 1.);\r\n}\r\n\r\nfloat boxOutradius() {\r\n return .5 * length(vec2(u_imageAspectRatio, 1.));\r\n}\r\n\r\nfloat spreadReach() {\r\n return .7 * pow(u_spread, 1.3 + 2.7 * u_spread);\r\n}\r\n\r\nfloat innerCircleMask(float radius, float inradius) {\r\n return 1. - smoothstep(.5 * inradius, 1.1 * inradius, radius);\r\n}\r\n\r\nfloat dispersionCurve(float t, float biasPow) {\r\n float mirroredT = u_bias < 0. ? 1. - t : t;\r\n float curved = pow(mirroredT, biasPow);\r\n return u_bias < 0. ? 1. - curved : curved;\r\n}\r\n\r\nvec2 getSpread(vec2 fromCenter, float radius, vec2 warpedUV, float edgeAA, float inradius, float outradius, float reach, out float outStrength) {\r\n float angleRad = radians(u_angle);\r\n vec2 uniformDir = vec2(cos(angleRad), sin(angleRad));\r\n\r\n float invInradius = 1. / inradius;\r\n vec2 radialDir = fromCenter * invInradius;\r\n float maxLen = (outradius + reach) * invInradius;\r\n float radialLen = radius * invInradius;\r\n if (radialLen > maxLen) radialDir *= maxLen / radialLen;\r\n vec2 spreadDir = mix(uniformDir, radialDir, u_perspective);\r\n\r\n float bandProximity = smoothstep(inradius * .8, inradius, radius);\r\n float lensCircleMaxing = bandProximity * u_lensCircle * u_lensCircle * u_lensCircle;\r\n spreadDir = mix(spreadDir, radialDir, lensCircleMaxing);\r\n\r\n vec2 warpedAbs = abs(warpedUV - .5);\r\n float inner = mix(1., smoothstep(0., mix(inradius, outradius, u_focusCenter), radius), u_focusCenter);\r\n float boxDist = max(warpedAbs.x, warpedAbs.y) * 2.;\r\n float outer = mix(1., 1. - min(boxDist, 1.), u_focusEdges);\r\n float strength = inner * outer;\r\n\r\n strength *= mix(1., mix(.15, .03, max(-u_lensBulge, 0.)), lensCircleMaxing);\r\n\r\n vec2 outside = max(warpedAbs - .5, 0.);\r\n float margin = max(reach * length(spreadDir) * (1. - u_lensCircle), edgeAA);\r\n strength *= 1. - smoothstep(0., margin, length(outside));\r\n\r\n outStrength = strength;\r\n vec2 axis = spreadDir * (reach * strength);\r\n\r\n if (u_noise > 0.) {\r\n float turn = (valueNoise(fromCenter * u_noiseFrequency * 18. + u_noiseOffset * 30.) - .5) * 2. * u_noise;\r\n float cs = cos(turn), sn = sin(turn);\r\n axis = mat2(cs, -sn, sn, cs) * axis;\r\n }\r\n\r\n axis.x /= u_imageAspectRatio;\r\n return axis;\r\n}\r\n\r\nvec2 lensWarp(vec2 fromCenter, float radius, float inradius, out float bulgeFade) {\r\n bulgeFade = 1.;\r\n if (u_lensBulge == 0. && u_lensCircle <= 0.) return fromCenter;\r\n if (radius < 1e-5) return fromCenter;\r\n \r\n float r = radius;\r\n\r\n if (u_lensBulge != 0.) {\r\n float rn = radius / inradius;\r\n float bulge = abs(u_lensBulge) * (u_lensBulge > 0. ? 1.4 : 1.2);\r\n if (u_lensBulge > 0.) bulgeFade = 1. - smoothstep(1.45, 1.53, rn * bulge);\r\n float map = u_lensBulge > 0.\r\n ? tan(min(rn * bulge, 1.53)) / tan(bulge)\r\n : atan(rn * tan(bulge)) / bulge;\r\n float bulgeScale = map / rn;\r\n fromCenter *= bulgeScale;\r\n r *= bulgeScale;\r\n }\r\n\r\n if (u_lensCircle > 0.) {\r\n vec2 dir = fromCenter / max(r, 1e-5);\r\n vec2 halfBox = vec2(u_imageAspectRatio, 1.) * .5;\r\n float rBox = min(halfBox.x / max(abs(dir.x), 1e-4), halfBox.y / max(abs(dir.y), 1e-4));\r\n float band = inradius * mix(.03, .2, .5 * (u_lensBulge + 1.));\r\n float innerEdge = inradius - band;\r\n float over = smoothstep(0., 1., (r - innerEdge) / band);\r\n float g = r + (rBox - inradius) * pow(over, 14.);\r\n fromCenter = dir * mix(r, g, u_lensCircle);\r\n }\r\n\r\n return fromCenter;\r\n}\r\n\r\nvoid main() {\r\n vec2 uv = v_imageUV;\r\n\r\n float invAspect = 1. / u_imageAspectRatio;\r\n float inradius = boxInradius();\r\n float outradius = boxOutradius();\r\n float reach = spreadReach();\r\n\r\n vec2 fromCenter = uv - .5;\r\n fromCenter.x *= u_imageAspectRatio;\r\n float radius = length(fromCenter);\r\n\r\n float bulgeFade;\r\n vec2 warpedFromCenter = lensWarp(fromCenter, radius, inradius, bulgeFade);\r\n vec2 baseUV = vec2(warpedFromCenter.x * invAspect, warpedFromCenter.y) + .5;\r\n\r\n vec2 baseDerivative = fwidth(baseUV);\r\n float edgeAA = clamp(2. * max(baseDerivative.x, baseDerivative.y), .001, .02);\r\n\r\n float spreadStrength;\r\n vec2 spreadAxis = getSpread(fromCenter, radius, baseUV, edgeAA, inradius, outradius, reach, spreadStrength);\r\n\r\n vec2 grainUV = vec2(0.);\r\n if (u_grainMixer > 0. || u_grainOverlay > 0.) {\r\n vec2 dudx = dFdx(v_imageUV);\r\n vec2 dudy = dFdy(v_imageUV);\r\n grainUV = (v_imageUV - .5) * (.8 / vec2(length(dudx), length(dudy))) + .5;\r\n }\r\n\r\n if (u_grainMixer > 0.) {\r\n float grainSeed = valueNoise(grainUV);\r\n vec2 jit = fract(grainSeed * vec2(157.31, 113.57)) * 2. - 1.;\r\n spreadAxis += jit * .3 * u_grainMixer * length(spreadAxis);\r\n }\r\n\r\n float count = floor(u_count);\r\n int countInteger = int(count);\r\n\r\n float invCount = 1. / count;\r\n float invSpan = 1. / max(count - 1., 1.);\r\n float hueBase = u_dispersionColor + .5;\r\n float biasPower = 1. + 2. * abs(u_bias);\r\n vec2 tapBias = .5 - vec2(u_imageX, u_imageY);\r\n\r\n float centerAmount = clamp(1. - u_dispersionShift, 0., 1.);\r\n float edgesAmount = clamp(1. + u_dispersionShift, 0., 1.);\r\n float dispersion = u_dispersion * mix(edgesAmount, centerAmount, innerCircleMask(radius, inradius));\r\n float dispersionPower = pow(dispersion, .8);\r\n\r\n float warpedRadius = length(warpedFromCenter);\r\n float swirlAngle = u_swirl * .4 * PI * spreadStrength * u_spread * min(1., inradius / max(warpedRadius, 1e-4));\r\n\r\n vec3 colorSum = vec3(0.);\r\n vec3 weightSum = vec3(0.);\r\n float coverSum = 0.;\r\n\r\n for (int i = 0; i < ${lensDistortionMeta.maxSamples}; i++) {\r\n if (i >= countInteger) break;\r\n\r\n float layer = float(i);\r\n float hue = hueBase + layer * invCount;\r\n\r\n float spread = layer * invSpan;\r\n if (u_bias != 0.) spread = dispersionCurve(spread, biasPower);\r\n\r\n // mix(axis, -axis, spread) is axis scaled by the signed position along the fan\r\n float fanPos = 1. - 2. * spread;\r\n vec2 tapUV = baseUV + spreadAxis * fanPos - .5;\r\n\r\n if (u_swirl != 0.) {\r\n tapUV.x *= u_imageAspectRatio;\r\n tapUV = rotate(tapUV, swirlAngle * fanPos);\r\n tapUV.x *= invAspect;\r\n }\r\n\r\n vec4 tap = sampleOverWhite(tapUV + tapBias);\r\n vec3 weight = 1. - dispersionPower * hueColor(hue);\r\n\r\n colorSum += tap.rgb * weight;\r\n weightSum += weight;\r\n coverSum += tap.a;\r\n }\r\n\r\n vec3 color = colorSum / max(weightSum, 1e-4);\r\n float coverAvg = coverSum * invCount;\r\n\r\n float ground = min(color.r, min(color.g, color.b));\r\n float alpha = max(coverAvg, 1. - ground);\r\n vec3 premult = max(color - (1. - alpha), 0.);\r\n fragColor = vec4(premult, alpha) * bulgeFade;\r\n\r\n if (u_grainOverlay > 0.) {\r\n float grain = valueNoise(rotate(grainUV, 1.) + vec2(3.));\r\n grain = mix(grain, valueNoise(rotate(grainUV, 2.) + vec2(-1.)), .5);\r\n grain = pow(grain, 1.3);\r\n float grainV = grain * 2. - 1.;\r\n float grainStrength = pow(u_grainOverlay * abs(grainV), .8) * fragColor.a;\r\n fragColor.rgb = mix(fragColor.rgb, vec3(step(0., grainV)) * fragColor.a, .35 * grainStrength);\r\n }\r\n}\r\n`;\r\n\r\nexport interface LensDistortionUniforms extends ShaderSizingUniforms {\r\n u_image: HTMLImageElement | string | undefined;\r\n u_spread: number;\r\n u_bias: number;\r\n u_angle: number;\r\n u_perspective: number;\r\n u_count: number;\r\n u_dispersion: number;\r\n u_dispersionShift: number;\r\n u_dispersionColor: number;\r\n u_focusCenter: number;\r\n u_focusEdges: number;\r\n u_swirl: number;\r\n u_noise: number;\r\n u_noiseFrequency: number;\r\n u_noiseOffset: number;\r\n u_lensBulge: number;\r\n u_lensCircle: number;\r\n u_grainMixer: number;\r\n u_grainOverlay: number;\r\n u_imageX: number;\r\n u_imageY: number;\r\n}\r\n\r\nexport interface LensDistortionParams extends ShaderSizingParams, ShaderMotionParams {\r\n image?: HTMLImageElement | string;\r\n spread?: number;\r\n bias?: number;\r\n angle?: number;\r\n perspective?: number;\r\n count?: number;\r\n dispersion?: number;\r\n dispersionShift?: number;\r\n dispersionColor?: number;\r\n focusCenter?: number;\r\n focusEdges?: number;\r\n swirl?: number;\r\n noise?: number;\r\n noiseFrequency?: number;\r\n noiseOffset?: number;\r\n lensBulge?: number;\r\n lensCircle?: number;\r\n grainMixer?: number;\r\n grainOverlay?: number;\r\n imageX?: number;\r\n imageY?: number;\r\n}\r\n"],
5
+ "mappings": ";;;;;AACA,eAAmE;AACnE,SAAS,WAAW,kBAAkB,iBAAiB;AAEhD,MAAM,qBAAqB;AAAA,EAChC,YAAY;AACd;AAiDO,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,EA8BlD,SAAS;AAAA,EACT,gBAAgB;AAAA,EAChB,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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAwLa,mBAAmB,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;",
6
+ "names": []
7
+ }