@paper-design/shaders 0.0.79 → 0.0.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -2
- package/dist/index.js +7 -2
- package/dist/index.js.map +2 -2
- package/dist/shaders/color-panels.d.ts +1 -1
- package/dist/shaders/color-panels.js +1 -1
- package/dist/shaders/color-panels.js.map +1 -1
- package/dist/shaders/dot-grid.d.ts +2 -2
- package/dist/shaders/dot-grid.js.map +1 -1
- package/dist/shaders/dot-orbit.d.ts +2 -2
- package/dist/shaders/dot-orbit.js.map +1 -1
- package/dist/shaders/fluted-glass.d.ts +6 -8
- package/dist/shaders/fluted-glass.js +18 -15
- package/dist/shaders/fluted-glass.js.map +2 -2
- package/dist/shaders/gem-smoke.d.ts +8 -6
- package/dist/shaders/gem-smoke.js +5 -17
- package/dist/shaders/gem-smoke.js.map +2 -2
- package/dist/shaders/god-rays.d.ts +3 -3
- package/dist/shaders/god-rays.js.map +1 -1
- package/dist/shaders/grain-gradient.d.ts +0 -2
- package/dist/shaders/grain-gradient.js +0 -2
- package/dist/shaders/grain-gradient.js.map +2 -2
- package/dist/shaders/halftone-cmyk.d.ts +11 -12
- package/dist/shaders/halftone-cmyk.js +5 -2
- package/dist/shaders/halftone-cmyk.js.map +2 -2
- package/dist/shaders/halftone-dots.d.ts +5 -7
- package/dist/shaders/halftone-dots.js +17 -17
- package/dist/shaders/halftone-dots.js.map +2 -2
- package/dist/shaders/heatmap.d.ts +2 -3
- package/dist/shaders/heatmap.js +0 -1
- package/dist/shaders/heatmap.js.map +2 -2
- package/dist/shaders/image-dithering.d.ts +5 -7
- package/dist/shaders/image-dithering.js +0 -2
- package/dist/shaders/image-dithering.js.map +2 -2
- package/dist/shaders/lens-distortion.d.ts +15 -15
- package/dist/shaders/lens-distortion.js +1 -1
- package/dist/shaders/lens-distortion.js.map +1 -1
- package/dist/shaders/liquid-metal.d.ts +2 -3
- package/dist/shaders/liquid-metal.js +4 -1
- package/dist/shaders/liquid-metal.js.map +2 -2
- package/dist/shaders/mesh-gradient.js +15 -11
- package/dist/shaders/mesh-gradient.js.map +2 -2
- package/dist/shaders/metaballs.d.ts +2 -2
- package/dist/shaders/metaballs.js +0 -1
- package/dist/shaders/metaballs.js.map +2 -2
- package/dist/shaders/neuro-noise.d.ts +1 -3
- package/dist/shaders/neuro-noise.js +0 -2
- package/dist/shaders/neuro-noise.js.map +2 -2
- package/dist/shaders/paper-texture.d.ts +65 -34
- package/dist/shaders/paper-texture.js +443 -164
- package/dist/shaders/paper-texture.js.map +2 -2
- package/dist/shaders/perlin-noise.d.ts +3 -3
- package/dist/shaders/perlin-noise.js +0 -6
- package/dist/shaders/perlin-noise.js.map +2 -2
- package/dist/shaders/pulsing-border.d.ts +2 -2
- package/dist/shaders/pulsing-border.js.map +1 -1
- package/dist/shaders/simplex-noise.d.ts +2 -3
- package/dist/shaders/simplex-noise.js +11 -19
- package/dist/shaders/simplex-noise.js.map +2 -2
- package/dist/shaders/smoke-ring.d.ts +1 -1
- package/dist/shaders/smoke-ring.js.map +1 -1
- package/dist/shaders/spiral.d.ts +3 -3
- package/dist/shaders/spiral.js +3 -1
- package/dist/shaders/spiral.js.map +2 -2
- package/dist/shaders/static-mesh-gradient.d.ts +2 -2
- package/dist/shaders/static-mesh-gradient.js +15 -12
- package/dist/shaders/static-mesh-gradient.js.map +2 -2
- package/dist/shaders/static-radial-gradient.d.ts +4 -4
- package/dist/shaders/static-radial-gradient.js +15 -11
- package/dist/shaders/static-radial-gradient.js.map +2 -2
- package/dist/shaders/swirl.d.ts +3 -3
- package/dist/shaders/swirl.js +3 -1
- package/dist/shaders/swirl.js.map +2 -2
- package/dist/shaders/voronoi.d.ts +3 -3
- package/dist/shaders/voronoi.js.map +1 -1
- package/dist/shaders/warp.d.ts +2 -4
- package/dist/shaders/warp.js +0 -2
- package/dist/shaders/warp.js.map +2 -2
- package/dist/shaders/water.d.ts +6 -6
- package/dist/shaders/water.js +4 -2
- package/dist/shaders/water.js.map +2 -2
- package/dist/shaders/waves.d.ts +3 -3
- package/dist/shaders/waves.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/shaders/halftone-dots.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, rotation2, proceduralHash21 } from '../shader-utils.js';\n\n/**\n * A halftone-dot image filter featuring customizable grids, color palettes, and dot styles.\n *\n * Fragment shader uniforms:\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_time (float): Animation time\n * - u_image (sampler2D): Source image texture\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n * - u_colorFront (vec4): Foreground color in RGBA\n * - u_colorBack (vec4): Background color in RGBA\n * - u_originalColors (bool): Use sampled image's original colors instead of colorFront\n * - u_type (float): Dot style (0 = classic, 1 = gooey, 2 = holes, 3 = soft)\n * - u_inverted (bool): Inverts the image luminance, doesn't affect the color scheme; not effective at zero contrast\n * - u_grid (float): Grid type (0 = square, 1 = hex)\n * - u_size (float): Grid size relative to the image box (0 to 1)\n * - u_radius (float): Maximum dot size relative to grid cell (0 to 2)\n * - u_contrast (float): Contrast applied to the sampled image (0 to 1)\n * - u_grainMixer (float): Strength of grain distortion applied to shape edges (0 to 1)\n * - u_grainOverlay (float): Post-processing black/white grain overlay (0 to 1)\n * - u_grainSize (float): Scale applied to both grain distortion and grain overlay (0 to 1)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_imageUV (vec2): Image UV coordinates with global sizing (rotation, scale, offset, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n *\n */\n\n// language=GLSL\nexport const halftoneDotsFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_rotation;\n\nuniform float u_time;\n\nuniform vec4 u_colorFront;\nuniform vec4 u_colorBack;\nuniform float u_radius;\nuniform float u_contrast;\n\nuniform sampler2D u_image;\nuniform float u_imageAspectRatio;\n\nuniform float u_size;\nuniform float u_grainMixer;\nuniform float u_grainOverlay;\nuniform float u_grainSize;\nuniform float u_grid;\nuniform bool u_originalColors;\nuniform bool u_inverted;\nuniform float u_type;\n\nin vec2 v_imageUV;\n\nout vec4 fragColor;\n\n${ declarePI }\n${ rotation2 }\n${ proceduralHash21 }\n\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = hash21(i);\n float b = hash21(i + vec2(1.0, 0.0));\n float c = hash21(i + vec2(0.0, 1.0));\n float d = hash21(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\n\nfloat 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 getCircle(vec2 uv, float r, float baseR) {\n r = mix(.25 * baseR, 0., r);\n float d = length(uv - .5);\n float aa = fwidth(d);\n return 1. - smoothstep(r - aa, r + aa, d);\n}\n\nfloat getCell(vec2 uv) {\n float insideX = step(0.0, uv.x) * (1.0 - step(1.0, uv.x));\n float insideY = step(0.0, uv.y) * (1.0 - step(1.0, uv.y));\n return insideX * insideY;\n}\n\nfloat getCircleWithHole(vec2 uv, float r, float baseR) {\n float cell = getCell(uv);\n\n r = mix(.75 * baseR, 0., r);\n float rMod = mod(r, .5);\n\n float d = length(uv - .5);\n float aa = fwidth(d);\n float circle = 1. - smoothstep(rMod - aa, rMod + aa, d);\n if (r < .5) {\n return circle;\n } else {\n return cell - circle;\n }\n}\n\nfloat getGooeyBall(vec2 uv, float r, float baseR) {\n float d = length(uv - .5);\n float sizeRadius = .3;\n if (u_grid == 1.) {\n sizeRadius = .42;\n }\n sizeRadius = mix(sizeRadius * baseR, 0., r);\n d = 1. - sst(0., sizeRadius, d);\n\n d = pow(d, 2. + baseR);\n return d;\n}\n\nfloat getSoftBall(vec2 uv, float r, float baseR) {\n float d = length(uv - .5);\n float sizeRadius = clamp(baseR, 0., 1.);\n sizeRadius = mix(.5 * sizeRadius, 0., r);\n d = 1. - lst(0., sizeRadius, d);\n float powRadius = 1. - lst(0., 2., baseR);\n d = pow(d, 4. + 3. * powRadius);\n return d;\n}\n\nfloat getUvFrame(vec2 uv, vec2 pad) {\n float aa = 0.0001;\n\n float left = smoothstep(-pad.x, -pad.x + aa, uv.x);\n float right = smoothstep(1.0 + pad.x, 1.0 + pad.x - aa, uv.x);\n float bottom = smoothstep(-pad.y, -pad.y + aa, uv.y);\n float top = smoothstep(1.0 + pad.y, 1.0 + pad.y - aa, uv.y);\n\n return left * right * bottom * top;\n}\n\nfloat sigmoid(float x, float k) {\n return 1.0 / (1.0 + exp(-k * (x - 0.5)));\n}\n\nfloat getLumAtPx(vec2 uv, float contrast) {\n vec4 tex = texture(u_image, uv);\n vec3 color = vec3(\n sigmoid(tex.r, contrast),\n sigmoid(tex.g, contrast),\n sigmoid(tex.b, contrast)\n );\n float lum = dot(vec3(0.2126, 0.7152, 0.0722), color);\n lum = mix(1., lum, tex.a);\n lum = u_inverted ? (1. - lum) : lum;\n return lum;\n}\n\nfloat getLumBall(vec2 p, vec2 pad, vec2 inCellOffset, float contrast, float baseR, float stepSize, out vec4 ballColor) {\n p += inCellOffset;\n vec2 uv_i = floor(p);\n vec2 uv_f = fract(p);\n vec2 samplingUV = (uv_i + .5 - inCellOffset) * pad + vec2(.5);\n float outOfFrame = getUvFrame(samplingUV, pad * stepSize);\n\n float lum = getLumAtPx(samplingUV, contrast);\n ballColor = texture(u_image, samplingUV);\n ballColor.rgb *= ballColor.a;\n ballColor *= outOfFrame;\n\n float ball = 0.;\n if (u_type == 0.) {\n // classic\n ball = getCircle(uv_f, lum, baseR);\n } else if (u_type == 1.) {\n // gooey\n ball = getGooeyBall(uv_f, lum, baseR);\n } else if (u_type == 2.) {\n // holes\n ball = getCircleWithHole(uv_f, lum, baseR);\n } else if (u_type == 3.) {\n // soft\n ball = getSoftBall(uv_f, lum, baseR);\n }\n\n return ball * outOfFrame;\n}\n\n\nvoid main() {\n\n float stepMultiplier = 1.;\n if (u_type == 0.) {\n // classic\n stepMultiplier = 2.;\n } else if (u_type == 1. || u_type == 3.) {\n // gooey & soft\n stepMultiplier = 6.;\n }\n\n float cellsPerSide = mix(300., 7., pow(u_size, .7));\n cellsPerSide /= stepMultiplier;\n float cellSizeY = 1. / cellsPerSide;\n vec2 pad = cellSizeY * vec2(1. / u_imageAspectRatio, 1.);\n if (u_type == 1. && u_grid == 1.) {\n // gooey diagonal grid works differently\n pad *= .7;\n }\n\n vec2 uv = v_imageUV;\n uv -= vec2(.5);\n uv /= pad;\n\n float contrast = mix(0., 15., pow(u_contrast, 1.5));\n float baseRadius = u_radius;\n if (u_originalColors == true) {\n contrast = mix(.1, 4., pow(u_contrast, 2.));\n baseRadius = 2. * pow(.5 * u_radius, .3);\n }\n\n float totalShape = 0.;\n vec3 totalColor = vec3(0.);\n float totalOpacity = 0.;\n\n vec4 ballColor;\n float shape;\n float stepSize = 1. / stepMultiplier;\n for (float x = -0.5; x < 0.5; x += stepSize) {\n for (float y = -0.5; y < 0.5; y += stepSize) {\n vec2 offset = vec2(x, y);\n\n if (u_grid == 1.) {\n float rowIndex = floor((y + .5) / stepSize);\n float colIndex = floor((x + .5) / stepSize);\n if (stepSize == 1.) {\n rowIndex = floor(uv.y + y + 1.);\n if (u_type == 1.) {\n colIndex = floor(uv.x + x + 1.);\n }\n }\n if (u_type == 1.) {\n if (mod(rowIndex + colIndex, 2.) == 1.) {\n continue;\n }\n } else {\n if (mod(rowIndex, 2.) == 1.) {\n offset.x += .5 * stepSize;\n }\n }\n }\n\n shape = getLumBall(uv, pad, offset, contrast, baseRadius, stepSize, ballColor);\n totalColor += ballColor.rgb * shape;\n totalShape += shape;\n totalOpacity += shape;\n }\n }\n\n const float eps = 1e-4;\n\n totalColor /= max(totalShape, eps);\n totalOpacity /= max(totalShape, eps);\n\n float finalShape = 0.;\n if (u_type == 0.) {\n finalShape = min(1., totalShape);\n } else if (u_type == 1.) {\n float aa = fwidth(totalShape);\n float th = .5;\n finalShape = smoothstep(th - aa, th + aa, totalShape);\n } else if (u_type == 2.) {\n finalShape = min(1., totalShape);\n } else if (u_type == 3.) {\n finalShape = totalShape;\n }\n\n vec2 grainSize = mix(2000., 200., u_grainSize) * vec2(1., 1. / u_imageAspectRatio);\n vec2 grainUV = v_imageUV - .5;\n grainUV *= grainSize;\n grainUV += .5;\n float grain = valueNoise(grainUV);\n grain = smoothstep(.55, .7 + .2 * u_grainMixer, grain);\n grain *= u_grainMixer;\n finalShape = mix(finalShape, 0., grain);\n\n vec3 color = vec3(0.);\n float opacity = 0.;\n\n if (u_originalColors == true) {\n color = totalColor * finalShape;\n opacity = totalOpacity * finalShape;\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n color = color + bgColor * (1. - opacity);\n opacity = opacity + u_colorBack.a * (1. - opacity);\n } 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\n color = fgColor * finalShape;\n opacity = fgOpacity * finalShape;\n color += bgColor * (1. - opacity);\n opacity += bgOpacity * (1. - opacity);\n }\n\n float grainOverlay = valueNoise(rotate(grainUV, 1.) + vec2(3.));\n grainOverlay = mix(grainOverlay, valueNoise(rotate(grainUV, 2.) + vec2(-1.)), .5);\n grainOverlay = pow(grainOverlay, 1.3);\n\n float grainOverlayV = grainOverlay * 2. - 1.;\n vec3 grainOverlayColor = vec3(step(0., grainOverlayV));\n float grainOverlayStrength = u_grainOverlay * abs(grainOverlayV);\n grainOverlayStrength = pow(grainOverlayStrength, .8);\n color = mix(color, grainOverlayColor, .5 * grainOverlayStrength);\n\n opacity += .5 * grainOverlayStrength;\n opacity = clamp(opacity, 0., 1.);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface HalftoneDotsUniforms extends ShaderSizingUniforms {\n u_image: HTMLImageElement | string | undefined;\n u_colorFront: [number, number, number, number];\n u_colorBack: [number, number, number, number];\n u_size: number;\n u_grid: (typeof HalftoneDotsGrids)[HalftoneDotsGrid];\n u_radius: number;\n u_contrast: number;\n u_originalColors: boolean;\n u_inverted: boolean;\n u_grainMixer: number;\n u_grainOverlay: number;\n u_grainSize: number;\n u_type: (typeof HalftoneDotsTypes)[HalftoneDotsType];\n}\n\nexport interface HalftoneDotsParams extends ShaderSizingParams, ShaderMotionParams {\n image?: HTMLImageElement | string;\n colorFront?: string;\n colorBack?: string;\n size?: number;\n grid?: HalftoneDotsGrid;\n radius?: number;\n contrast?: number;\n originalColors?: boolean;\n inverted?: boolean;\n grainMixer?: number;\n grainOverlay?: number;\n grainSize?: number;\n type?: HalftoneDotsType;\n}\n\nexport const HalftoneDotsTypes = {\n classic: 0,\n gooey: 1,\n holes: 2,\n soft: 3,\n} as const;\n\nexport type HalftoneDotsType = keyof typeof HalftoneDotsTypes;\n\nexport const HalftoneDotsGrids = {\n square: 0,\n hex: 1,\n} as const;\n\nexport type HalftoneDotsGrid = keyof typeof HalftoneDotsGrids;\n"],
|
|
5
|
-
"mappings": ";;;;;AACA,eAAmE;AACnE,SAAS,WAAW,WAAW,wBAAwB;
|
|
4
|
+
"sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, rotation2, proceduralHash21 } from '../shader-utils.js';\n\n/**\n * A halftone-dot image filter featuring customizable grids, color palettes, and dot styles.\n *\n * Fragment shader uniforms:\n * - u_image (sampler2D): Source image texture\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n * - u_colorFront (vec4): Foreground color in RGBA, needs originalColors off\n * - u_colorBack (vec4): Background color in RGBA\n * - u_originalColors (bool): Use sampled image's original colors instead of colorFront\n * - u_type (float): Dot style (0 = classic, 1 = gooey, 2 = holes, 3 = soft)\n * - u_inverted (bool): Inverts the image luminance, doesn't affect the color scheme, needs contrast > 0\n * - u_grid (float): Grid type (0 = square, 1 = hex)\n * - u_size (float): Grid size relative to the image box (0 to 1)\n * - u_radius (float): Maximum dot size relative to grid cell (0 to 2)\n * - u_contrast (float): Contrast applied to the sampled image, at 0 the luminance is flat so all the dots get equal size (0 to 1)\n * - u_grainMixer (float): Strength of grain distortion applied to shape edges (0 to 1)\n * - u_grainOverlay (float): Post-processing black/white grain overlay (0 to 1)\n * - u_grainSize (float): Scale applied to both grain distortion and grain overlay, needs grainMixer or grainOverlay > 0 (0 to 1)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_imageUV (vec2): Image UV coordinates with global sizing (rotation, scale, offset, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.1 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n *\n */\n\n// language=GLSL\nexport const halftoneDotsFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform vec4 u_colorFront;\nuniform vec4 u_colorBack;\nuniform float u_radius;\nuniform float u_contrast;\n\nuniform sampler2D u_image;\nuniform float u_imageAspectRatio;\n\nuniform float u_size;\nuniform float u_grainMixer;\nuniform float u_grainOverlay;\nuniform float u_grainSize;\nuniform float u_grid;\nuniform bool u_originalColors;\nuniform bool u_inverted;\nuniform float u_type;\n\nin vec2 v_imageUV;\n\nout vec4 fragColor;\n\n${ declarePI }\n${ rotation2 }\n${ proceduralHash21 }\n\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = hash21(i);\n float b = hash21(i + vec2(1.0, 0.0));\n float c = hash21(i + vec2(0.0, 1.0));\n float d = hash21(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\n\nfloat 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 getCircle(vec2 uv, float r, float baseR) {\n r = mix(.25 * baseR, 0., r);\n float d = length(uv - .5);\n float aa = fwidth(d);\n return 1. - smoothstep(r - aa, r + aa, d);\n}\n\nfloat getCell(vec2 uv) {\n float insideX = step(0.0, uv.x) * (1.0 - step(1.0, uv.x));\n float insideY = step(0.0, uv.y) * (1.0 - step(1.0, uv.y));\n return insideX * insideY;\n}\n\nfloat getCircleWithHole(vec2 uv, float r, float baseR) {\n float cell = getCell(uv);\n\n r = mix(.75 * baseR, 0., r);\n float rMod = mod(r, .5);\n\n float d = length(uv - .5);\n float aa = fwidth(d);\n float circle = 1. - smoothstep(rMod - aa, rMod + aa, d);\n if (r < .5) {\n return circle;\n } else {\n return cell - circle;\n }\n}\n\nfloat getGooeyBall(vec2 uv, float r, float baseR) {\n float d = length(uv - .5);\n float sizeRadius = .3;\n if (u_grid == 1.) {\n sizeRadius = .42;\n }\n sizeRadius = mix(sizeRadius * baseR, 0., r);\n d = 1. - sst(0., sizeRadius, d);\n\n d = pow(d, 2. + baseR);\n return d;\n}\n\nfloat getSoftBall(vec2 uv, float r, float baseR) {\n float d = length(uv - .5);\n float sizeRadius = clamp(baseR, 0., 1.);\n sizeRadius = mix(.5 * sizeRadius, 0., r);\n d = 1. - lst(0., sizeRadius, d);\n float powRadius = 1. - lst(0., 2., baseR);\n d = pow(d, 4. + 3. * powRadius);\n return d;\n}\n\nfloat getUvFrame(vec2 uv, vec2 pad) {\n float aa = 0.0001;\n\n float left = smoothstep(-pad.x, -pad.x + aa, uv.x);\n float right = smoothstep(1.0 + pad.x, 1.0 + pad.x - aa, uv.x);\n float bottom = smoothstep(-pad.y, -pad.y + aa, uv.y);\n float top = smoothstep(1.0 + pad.y, 1.0 + pad.y - aa, uv.y);\n\n return left * right * bottom * top;\n}\n\nfloat sigmoid(float x, float k) {\n return 1.0 / (1.0 + exp(-k * (x - 0.5)));\n}\n\nfloat getLumAtPx(vec2 uv, float contrast) {\n vec4 tex = texture(u_image, uv);\n vec3 color = vec3(\n sigmoid(tex.r, contrast),\n sigmoid(tex.g, contrast),\n sigmoid(tex.b, contrast)\n );\n float lum = dot(vec3(0.2126, 0.7152, 0.0722), color);\n lum = mix(1., lum, tex.a);\n lum = u_inverted ? (1. - lum) : lum;\n return lum;\n}\n\nfloat getLumBall(vec2 p, vec2 pad, vec2 inCellOffset, float contrast, float baseR, float stepSize, out vec4 ballColor) {\n p += inCellOffset;\n vec2 uv_i = floor(p);\n vec2 uv_f = fract(p);\n vec2 samplingUV = (uv_i + .5 - inCellOffset) * pad + vec2(.5);\n float outOfFrame = getUvFrame(samplingUV, pad * stepSize);\n\n float lum = getLumAtPx(samplingUV, contrast);\n ballColor = texture(u_image, samplingUV);\n ballColor.rgb *= ballColor.a;\n ballColor *= outOfFrame;\n\n float ball = 0.;\n if (u_type == 0.) {\n // classic\n ball = getCircle(uv_f, lum, baseR);\n } else if (u_type == 1.) {\n // gooey\n ball = getGooeyBall(uv_f, lum, baseR);\n } else if (u_type == 2.) {\n // holes\n ball = getCircleWithHole(uv_f, lum, baseR);\n } else if (u_type == 3.) {\n // soft\n ball = getSoftBall(uv_f, lum, baseR);\n }\n\n return ball * outOfFrame;\n}\n\n\nvoid main() {\n\n float stepMultiplier = 1.;\n if (u_type == 0.) {\n // classic\n stepMultiplier = 2.;\n } else if (u_type == 1. || u_type == 3.) {\n // gooey & soft\n stepMultiplier = 6.;\n }\n\n float cellsPerSide = mix(300., 7., pow(u_size, .7));\n cellsPerSide /= stepMultiplier;\n float cellSizeY = 1. / cellsPerSide;\n vec2 pad = cellSizeY * vec2(1. / u_imageAspectRatio, 1.);\n if (u_type == 1. && u_grid == 1.) {\n // gooey diagonal grid works differently\n pad *= .7;\n }\n\n vec2 uv = v_imageUV;\n uv -= vec2(.5);\n uv /= pad;\n\n float contrast = mix(0., 15., pow(u_contrast, 1.5));\n float baseRadius = u_radius;\n if (u_originalColors == true) {\n contrast = mix(.1, 4., pow(u_contrast, 2.));\n baseRadius = 2. * pow(.5 * u_radius, .3);\n }\n\n float totalShape = 0.;\n vec3 totalColor = vec3(0.);\n float totalOpacity = 0.;\n\n vec4 ballColor;\n float shape;\n float stepSize = 1. / stepMultiplier;\n for (float x = -0.5; x < 0.5; x += stepSize) {\n for (float y = -0.5; y < 0.5; y += stepSize) {\n vec2 offset = vec2(x, y);\n\n if (u_grid == 1.) {\n float rowIndex = floor((y + .5) / stepSize);\n float colIndex = floor((x + .5) / stepSize);\n if (stepSize == 1.) {\n rowIndex = floor(uv.y + y + 1.);\n if (u_type == 1.) {\n colIndex = floor(uv.x + x + 1.);\n }\n }\n if (u_type == 1.) {\n if (mod(rowIndex + colIndex, 2.) == 1.) {\n continue;\n }\n } else {\n if (mod(rowIndex, 2.) == 1.) {\n offset.x += .5 * stepSize;\n }\n }\n }\n\n shape = getLumBall(uv, pad, offset, contrast, baseRadius, stepSize, ballColor);\n totalColor += ballColor.rgb * shape;\n totalShape += shape;\n totalOpacity += shape;\n }\n }\n\n const float eps = 1e-4;\n\n totalColor /= max(totalShape, eps);\n totalOpacity /= max(totalShape, eps);\n\n float finalShape = 0.;\n if (u_type == 0.) {\n finalShape = min(1., totalShape);\n } else if (u_type == 1.) {\n float aa = fwidth(totalShape);\n float th = .5;\n finalShape = smoothstep(th - aa, th + aa, totalShape);\n } else if (u_type == 2.) {\n finalShape = min(1., totalShape);\n } else if (u_type == 3.) {\n finalShape = totalShape;\n }\n\n vec2 grainSize = mix(2000., 200., u_grainSize) * vec2(1., 1. / u_imageAspectRatio);\n vec2 grainUV = v_imageUV - .5;\n grainUV *= grainSize;\n grainUV += .5;\n if (u_grainMixer > 0.) {\n float grain = valueNoise(grainUV);\n grain = smoothstep(.55, .7 + .2 * u_grainMixer, grain);\n grain *= u_grainMixer;\n finalShape = mix(finalShape, 0., grain);\n }\n\n vec3 color = vec3(0.);\n float opacity = 0.;\n\n if (u_originalColors == true) {\n color = totalColor * finalShape;\n opacity = totalOpacity * finalShape;\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n color = color + bgColor * (1. - opacity);\n opacity = opacity + u_colorBack.a * (1. - opacity);\n } 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\n color = fgColor * finalShape;\n opacity = fgOpacity * finalShape;\n color += bgColor * (1. - opacity);\n opacity += bgOpacity * (1. - opacity);\n }\n\n if (u_grainOverlay > 0.) {\n float grainOverlay = valueNoise(rotate(grainUV, 1.) + vec2(3.));\n grainOverlay = mix(grainOverlay, valueNoise(rotate(grainUV, 2.) + vec2(-1.)), .5);\n grainOverlay = pow(grainOverlay, 1.3);\n\n float grainOverlayV = grainOverlay * 2. - 1.;\n vec3 grainOverlayColor = vec3(step(0., grainOverlayV));\n float grainOverlayStrength = u_grainOverlay * abs(grainOverlayV);\n grainOverlayStrength = pow(grainOverlayStrength, .8);\n color = mix(color, grainOverlayColor, .5 * grainOverlayStrength);\n\n opacity += .5 * grainOverlayStrength;\n }\n opacity = clamp(opacity, 0., 1.);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface HalftoneDotsUniforms extends ShaderSizingUniforms {\n u_image: HTMLImageElement | string | undefined;\n u_colorFront: [number, number, number, number];\n u_colorBack: [number, number, number, number];\n u_size: number;\n u_grid: (typeof HalftoneDotsGrids)[HalftoneDotsGrid];\n u_radius: number;\n u_contrast: number;\n u_originalColors: boolean;\n u_inverted: boolean;\n u_grainMixer: number;\n u_grainOverlay: number;\n u_grainSize: number;\n u_type: (typeof HalftoneDotsTypes)[HalftoneDotsType];\n}\n\nexport interface HalftoneDotsParams extends ShaderSizingParams, ShaderMotionParams {\n image?: HTMLImageElement | string;\n colorFront?: string;\n colorBack?: string;\n size?: number;\n grid?: HalftoneDotsGrid;\n radius?: number;\n contrast?: number;\n originalColors?: boolean;\n inverted?: boolean;\n grainMixer?: number;\n grainOverlay?: number;\n grainSize?: number;\n type?: HalftoneDotsType;\n}\n\nexport const HalftoneDotsTypes = {\n classic: 0,\n gooey: 1,\n holes: 2,\n soft: 3,\n} as const;\n\nexport type HalftoneDotsType = keyof typeof HalftoneDotsTypes;\n\nexport const HalftoneDotsGrids = {\n square: 0,\n hex: 1,\n} as const;\n\nexport type HalftoneDotsGrid = keyof typeof HalftoneDotsGrids;\n"],
|
|
5
|
+
"mappings": ";;;;;AACA,eAAmE;AACnE,SAAS,WAAW,WAAW,wBAAwB;AAuChD,MAAM,6BAAqC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwB/C,SAAU;AAAA,EACV,SAAU;AAAA,EACV,gBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiTb,MAAM,oBAAoB;AAAA,EAC/B,SAAS;AAAA,EACT,OAAO;AAAA,EACP,OAAO;AAAA,EACP,MAAM;AACR;AAIO,MAAM,oBAAoB;AAAA,EAC/B,QAAQ;AAAA,EACR,KAAK;AACP;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -11,12 +11,11 @@ export declare const heatmapMeta: {
|
|
|
11
11
|
* Fragment shader uniforms:
|
|
12
12
|
* - u_time (float): Animation time
|
|
13
13
|
* - u_image (sampler2D): Pre-processed source image texture (R = contour, G = outer blur, B = inner blur)
|
|
14
|
-
* - u_imageAspectRatio (float): Aspect ratio of the source image
|
|
15
14
|
* - u_colorBack (vec4): Background color in RGBA
|
|
16
15
|
* - u_colors (vec4[]): Up to 10 heatmap colors in RGBA
|
|
17
16
|
* - u_colorsCount (float): Number of active colors
|
|
18
17
|
* - u_contour (float): Heat intensity near the edges of the input shape (0 to 1)
|
|
19
|
-
* - u_angle (float): Direction of the heatwaves in degrees (0 to 360)
|
|
18
|
+
* - u_angle (float): Direction of the heatwaves in degrees, needs innerGlow or outerGlow > 0 (0 to 360)
|
|
20
19
|
* - u_noise (float): Grain applied across the entire graphic (0 to 1)
|
|
21
20
|
* - u_innerGlow (float): Size of the heated area inside the input shape (0 to 1)
|
|
22
21
|
* - u_outerGlow (float): Size of the heated area outside the input shape (0 to 1)
|
|
@@ -33,7 +32,7 @@ export declare const heatmapMeta: {
|
|
|
33
32
|
* - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas
|
|
34
33
|
* - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas
|
|
35
34
|
* - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)
|
|
36
|
-
* - u_scale (float): Overall zoom level of the graphics (0.
|
|
35
|
+
* - u_scale (float): Overall zoom level of the graphics (0.1 to 4)
|
|
37
36
|
* - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)
|
|
38
37
|
* - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)
|
|
39
38
|
* - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)
|
package/dist/shaders/heatmap.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/shaders/heatmap.ts"],
|
|
4
|
-
"sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport type { ShaderSizingParams, ShaderSizingUniforms } from '../shader-sizing.js';\n\nexport const heatmapMeta = {\n maxColorCount: 10,\n} as const;\n\n/**\n * A glowing gradient of colors flowing through an input shape.\n * The effect creates a smoothly animated wave of intensity across the image.\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_image (sampler2D): Pre-processed source image texture (R = contour, G = outer blur, B = inner blur)\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n * - u_colorBack (vec4): Background color in RGBA\n * - u_colors (vec4[]): Up to 10 heatmap colors in RGBA\n * - u_colorsCount (float): Number of active colors\n * - u_contour (float): Heat intensity near the edges of the input shape (0 to 1)\n * - u_angle (float): Direction of the heatwaves in degrees (0 to 360)\n * - u_noise (float): Grain applied across the entire graphic (0 to 1)\n * - u_innerGlow (float): Size of the heated area inside the input shape (0 to 1)\n * - u_outerGlow (float): Size of the heated area outside the input shape (0 to 1)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_imageUV (vec2): UV coordinates for sampling the source image, with fit, scale, rotation, and offset applied\n * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n *\n */\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\nfloat blurEdge3x3(sampler2D tex, vec2 uv, vec2 dudx, vec2 dudy, float radius, float centerSample) {\n vec2 texel = 1.0 / vec2(textureSize(tex, 0));\n vec2 r = radius * texel;\n\n float w1 = 1.0, w2 = 2.0, w4 = 4.0;\n float norm = 16.0;\n float sum = w4 * centerSample;\n\n sum += w2 * textureGrad(tex, uv + vec2(0.0, -r.y), dudx, dudy).g;\n sum += w2 * textureGrad(tex, uv + vec2(0.0, r.y), dudx, dudy).g;\n sum += w2 * textureGrad(tex, uv + vec2(-r.x, 0.0), dudx, dudy).g;\n sum += w2 * textureGrad(tex, uv + vec2(r.x, 0.0), dudx, dudy).g;\n\n sum += w1 * textureGrad(tex, uv + vec2(-r.x, -r.y), dudx, dudy).g;\n sum += w1 * textureGrad(tex, uv + vec2(r.x, -r.y), dudx, dudy).g;\n sum += w1 * textureGrad(tex, uv + vec2(-r.x, r.y), dudx, dudy).g;\n sum += w1 * textureGrad(tex, uv + vec2(r.x, r.y), dudx, dudy).g;\n\n return sum / norm;\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 vec2 dudx = dFdx(imgUV);\n vec2 dudy = dFdy(imgUV);\n\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\n img[1] = blurEdge3x3(u_image, imgUV, dudx, dudy, 8., img[1]);\n\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 // 1) Draw original image once, no filters\n ctx.fillStyle = 'white';\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n ctx.drawImage(image, padding, padding, imgWidth, imgHeight);\n\n const { width, height } = canvas;\n const srcImageData = ctx.getImageData(0, 0, width, height);\n const src = srcImageData.data; // RGBA\n\n // 2) Build grayscale array (luma)\n const totalPixels = width * height;\n const gray = new Uint8ClampedArray(totalPixels);\n for (let i = 0; i < totalPixels; i++) {\n const px = i * 4;\n const r = src[px] ?? 0;\n const g = src[px + 1] ?? 0;\n const b = src[px + 2] ?? 0;\n // Standard luma conversion\n gray[i] = (0.299 * r + 0.587 * g + 0.114 * b) | 0;\n }\n\n // 3) Blur grayscale for each \"filter\" you previously used\n const bigBlurRadius = maxBlur;\n const innerBlurRadius = Math.max(1, Math.round(0.12 * maxBlur));\n const contourRadius = 5;\n\n const bigBlurGray = multiPassBlurGray(gray, width, height, bigBlurRadius, 3);\n const innerBlurGray = multiPassBlurGray(gray, width, height, innerBlurRadius, 3);\n const contourGray = multiPassBlurGray(gray, width, height, contourRadius, 1);\n\n // 4) Combine into final ImageData\n const processedImageData = ctx.createImageData(width, height);\n const dst = processedImageData.data;\n\n for (let i = 0; i < totalPixels; i++) {\n const px = i * 4;\n dst[px] = contourGray[i] ?? 0;\n dst[px + 1] = bigBlurGray[i] ?? 0;\n dst[px + 2] = innerBlurGray[i] ?? 0;\n dst[px + 3] = 255;\n }\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 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\n/**\n * Fast box blur for grayscale images using an integral image.\n * gray: Uint8ClampedArray of length width * height\n * radius: blur radius in pixels\n */\nfunction blurGray(gray: Uint8ClampedArray, width: number, height: number, radius: number): Uint8ClampedArray {\n if (radius <= 0) {\n return gray.slice();\n }\n\n const out = new Uint8ClampedArray(width * height);\n const integral = new Uint32Array(width * height);\n\n // Build integral image\n for (let y = 0; y < height; y++) {\n let rowSum = 0;\n for (let x = 0; x < width; x++) {\n const idx = y * width + x;\n const v = gray[idx] ?? 0;\n rowSum += v;\n integral[idx] = rowSum + (y > 0 ? (integral[idx - width] ?? 0) : 0);\n }\n }\n\n // Blur using integral image\n for (let y = 0; y < height; y++) {\n const y1 = Math.max(0, y - radius);\n const y2 = Math.min(height - 1, y + radius);\n for (let x = 0; x < width; x++) {\n const x1 = Math.max(0, x - radius);\n const x2 = Math.min(width - 1, x + radius);\n\n const idxA = y2 * width + x2;\n const idxB = y2 * width + (x1 - 1);\n const idxC = (y1 - 1) * width + x2;\n const idxD = (y1 - 1) * width + (x1 - 1);\n\n const A = integral[idxA] ?? 0;\n const B = x1 > 0 ? (integral[idxB] ?? 0) : 0;\n const C = y1 > 0 ? (integral[idxC] ?? 0) : 0;\n const D = x1 > 0 && y1 > 0 ? (integral[idxD] ?? 0) : 0;\n\n const sum = A - B - C + D;\n const area = (x2 - x1 + 1) * (y2 - y1 + 1);\n out[y * width + x] = Math.round(sum / area);\n }\n }\n\n return out;\n}\n\nfunction multiPassBlurGray(\n gray: Uint8ClampedArray,\n width: number,\n height: number,\n radius: number,\n passes: number\n): Uint8ClampedArray {\n if (radius <= 0 || passes <= 1) {\n return blurGray(gray, width, height, radius);\n }\n\n let input = gray;\n let tmp: Uint8ClampedArray = gray;\n\n for (let p = 0; p < passes; p++) {\n tmp = blurGray(input, width, height, radius);\n input = tmp;\n }\n\n return tmp;\n}\n\nexport interface HeatmapUniforms extends ShaderSizingUniforms {\n u_image: HTMLImageElement | string;\n u_contour: number;\n u_angle: number;\n u_noise: number;\n u_innerGlow: number;\n u_outerGlow: number;\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n}\n\nexport interface HeatmapParams extends ShaderSizingParams, ShaderMotionParams {\n image: HTMLImageElement | string;\n contour?: number;\n angle?: number;\n noise?: number;\n innerGlow?: number;\n outerGlow?: number;\n colorBack?: string;\n colors?: string[];\n}\n"],
|
|
5
|
-
"mappings": ";;;;;AAIO,MAAM,cAAc;AAAA,EACzB,eAAe;AACjB;
|
|
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/**\n * A glowing gradient of colors flowing through an input shape.\n * The effect creates a smoothly animated wave of intensity across the image.\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_image (sampler2D): Pre-processed source image texture (R = contour, G = outer blur, B = inner blur)\n * - u_colorBack (vec4): Background color in RGBA\n * - u_colors (vec4[]): Up to 10 heatmap colors in RGBA\n * - u_colorsCount (float): Number of active colors\n * - u_contour (float): Heat intensity near the edges of the input shape (0 to 1)\n * - u_angle (float): Direction of the heatwaves in degrees, needs innerGlow or outerGlow > 0 (0 to 360)\n * - u_noise (float): Grain applied across the entire graphic (0 to 1)\n * - u_innerGlow (float): Size of the heated area inside the input shape (0 to 1)\n * - u_outerGlow (float): Size of the heated area outside the input shape (0 to 1)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_imageUV (vec2): UV coordinates for sampling the source image, with fit, scale, rotation, and offset applied\n * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.1 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n *\n */\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;\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\nfloat blurEdge3x3(sampler2D tex, vec2 uv, vec2 dudx, vec2 dudy, float radius, float centerSample) {\n vec2 texel = 1.0 / vec2(textureSize(tex, 0));\n vec2 r = radius * texel;\n\n float w1 = 1.0, w2 = 2.0, w4 = 4.0;\n float norm = 16.0;\n float sum = w4 * centerSample;\n\n sum += w2 * textureGrad(tex, uv + vec2(0.0, -r.y), dudx, dudy).g;\n sum += w2 * textureGrad(tex, uv + vec2(0.0, r.y), dudx, dudy).g;\n sum += w2 * textureGrad(tex, uv + vec2(-r.x, 0.0), dudx, dudy).g;\n sum += w2 * textureGrad(tex, uv + vec2(r.x, 0.0), dudx, dudy).g;\n\n sum += w1 * textureGrad(tex, uv + vec2(-r.x, -r.y), dudx, dudy).g;\n sum += w1 * textureGrad(tex, uv + vec2(r.x, -r.y), dudx, dudy).g;\n sum += w1 * textureGrad(tex, uv + vec2(-r.x, r.y), dudx, dudy).g;\n sum += w1 * textureGrad(tex, uv + vec2(r.x, r.y), dudx, dudy).g;\n\n return sum / norm;\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 vec2 dudx = dFdx(imgUV);\n vec2 dudy = dFdy(imgUV);\n\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\n img[1] = blurEdge3x3(u_image, imgUV, dudx, dudy, 8., img[1]);\n\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 // 1) Draw original image once, no filters\n ctx.fillStyle = 'white';\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n ctx.drawImage(image, padding, padding, imgWidth, imgHeight);\n\n const { width, height } = canvas;\n const srcImageData = ctx.getImageData(0, 0, width, height);\n const src = srcImageData.data; // RGBA\n\n // 2) Build grayscale array (luma)\n const totalPixels = width * height;\n const gray = new Uint8ClampedArray(totalPixels);\n for (let i = 0; i < totalPixels; i++) {\n const px = i * 4;\n const r = src[px] ?? 0;\n const g = src[px + 1] ?? 0;\n const b = src[px + 2] ?? 0;\n // Standard luma conversion\n gray[i] = (0.299 * r + 0.587 * g + 0.114 * b) | 0;\n }\n\n // 3) Blur grayscale for each \"filter\" you previously used\n const bigBlurRadius = maxBlur;\n const innerBlurRadius = Math.max(1, Math.round(0.12 * maxBlur));\n const contourRadius = 5;\n\n const bigBlurGray = multiPassBlurGray(gray, width, height, bigBlurRadius, 3);\n const innerBlurGray = multiPassBlurGray(gray, width, height, innerBlurRadius, 3);\n const contourGray = multiPassBlurGray(gray, width, height, contourRadius, 1);\n\n // 4) Combine into final ImageData\n const processedImageData = ctx.createImageData(width, height);\n const dst = processedImageData.data;\n\n for (let i = 0; i < totalPixels; i++) {\n const px = i * 4;\n dst[px] = contourGray[i] ?? 0;\n dst[px + 1] = bigBlurGray[i] ?? 0;\n dst[px + 2] = innerBlurGray[i] ?? 0;\n dst[px + 3] = 255;\n }\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 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\n/**\n * Fast box blur for grayscale images using an integral image.\n * gray: Uint8ClampedArray of length width * height\n * radius: blur radius in pixels\n */\nfunction blurGray(gray: Uint8ClampedArray, width: number, height: number, radius: number): Uint8ClampedArray {\n if (radius <= 0) {\n return gray.slice();\n }\n\n const out = new Uint8ClampedArray(width * height);\n const integral = new Uint32Array(width * height);\n\n // Build integral image\n for (let y = 0; y < height; y++) {\n let rowSum = 0;\n for (let x = 0; x < width; x++) {\n const idx = y * width + x;\n const v = gray[idx] ?? 0;\n rowSum += v;\n integral[idx] = rowSum + (y > 0 ? (integral[idx - width] ?? 0) : 0);\n }\n }\n\n // Blur using integral image\n for (let y = 0; y < height; y++) {\n const y1 = Math.max(0, y - radius);\n const y2 = Math.min(height - 1, y + radius);\n for (let x = 0; x < width; x++) {\n const x1 = Math.max(0, x - radius);\n const x2 = Math.min(width - 1, x + radius);\n\n const idxA = y2 * width + x2;\n const idxB = y2 * width + (x1 - 1);\n const idxC = (y1 - 1) * width + x2;\n const idxD = (y1 - 1) * width + (x1 - 1);\n\n const A = integral[idxA] ?? 0;\n const B = x1 > 0 ? (integral[idxB] ?? 0) : 0;\n const C = y1 > 0 ? (integral[idxC] ?? 0) : 0;\n const D = x1 > 0 && y1 > 0 ? (integral[idxD] ?? 0) : 0;\n\n const sum = A - B - C + D;\n const area = (x2 - x1 + 1) * (y2 - y1 + 1);\n out[y * width + x] = Math.round(sum / area);\n }\n }\n\n return out;\n}\n\nfunction multiPassBlurGray(\n gray: Uint8ClampedArray,\n width: number,\n height: number,\n radius: number,\n passes: number\n): Uint8ClampedArray {\n if (radius <= 0 || passes <= 1) {\n return blurGray(gray, width, height, radius);\n }\n\n let input = gray;\n let tmp: Uint8ClampedArray = gray;\n\n for (let p = 0; p < passes; p++) {\n tmp = blurGray(input, width, height, radius);\n input = tmp;\n }\n\n return tmp;\n}\n\nexport interface HeatmapUniforms extends ShaderSizingUniforms {\n u_image: HTMLImageElement | string;\n u_contour: number;\n u_angle: number;\n u_noise: number;\n u_innerGlow: number;\n u_outerGlow: number;\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n}\n\nexport interface HeatmapParams extends ShaderSizingParams, ShaderMotionParams {\n image: HTMLImageElement | string;\n contour?: number;\n angle?: number;\n noise?: number;\n innerGlow?: number;\n outerGlow?: number;\n colorBack?: string;\n colors?: string[];\n}\n"],
|
|
5
|
+
"mappings": ";;;;;AAIO,MAAM,cAAc;AAAA,EACzB,eAAe;AACjB;AAuCO,MAAM,wBAAgC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAWpB,YAAY,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAwN1B,YAAY,gBAAgB,CAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwBhD,SAAS,mBAAmB,MAA8C;AAC/E,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,QAAM,aAAa;AAEnB,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,QAAQ,IAAI,MAAM;AACxB,UAAM,cAAc;AAEpB,UAAM,iBAAiB,QAAQ,MAAM;AACnC,UAAI,OAAO,SAAS,WAAW,KAAK,SAAS,MAAM,IAAI,KAAK,SAAS,iBAAiB;AAEpF,cAAM,QAAQ;AACd,cAAM,SAAS;AAAA,MACjB;AAEA,YAAM,QAAQ,MAAM,eAAe,MAAM;AAEzC,YAAM,UAAU,KAAK,MAAM,aAAa,IAAI;AAC5C,YAAM,UAAU,KAAK,KAAK,UAAU,GAAG;AACvC,UAAI,WAAW;AACf,UAAI,YAAY;AAChB,UAAI,QAAQ,GAAG;AACb,oBAAY,KAAK,MAAM,aAAa,KAAK;AAAA,MAC3C,OAAO;AACL,mBAAW,KAAK,MAAM,aAAa,KAAK;AAAA,MAC1C;AAEA,aAAO,QAAQ,WAAW,IAAI;AAC9B,aAAO,SAAS,YAAY,IAAI;AAEhC,YAAM,MAAM,OAAO,WAAW,MAAM,EAAE,oBAAoB,KAAK,CAAC;AAChE,UAAI,CAAC,KAAK;AACR,cAAM,IAAI,MAAM,iCAAiC;AAAA,MACnD;AAGA,UAAI,YAAY;AAChB,UAAI,SAAS,GAAG,GAAG,OAAO,OAAO,OAAO,MAAM;AAC9C,UAAI,UAAU,OAAO,SAAS,SAAS,UAAU,SAAS;AAE1D,YAAM,EAAE,OAAO,OAAO,IAAI;AAC1B,YAAM,eAAe,IAAI,aAAa,GAAG,GAAG,OAAO,MAAM;AACzD,YAAM,MAAM,aAAa;AAGzB,YAAM,cAAc,QAAQ;AAC5B,YAAM,OAAO,IAAI,kBAAkB,WAAW;AAC9C,eAAS,IAAI,GAAG,IAAI,aAAa,KAAK;AACpC,cAAM,KAAK,IAAI;AACf,cAAM,IAAI,IAAI,EAAE,KAAK;AACrB,cAAM,IAAI,IAAI,KAAK,CAAC,KAAK;AACzB,cAAM,IAAI,IAAI,KAAK,CAAC,KAAK;AAEzB,aAAK,CAAC,IAAK,QAAQ,IAAI,QAAQ,IAAI,QAAQ,IAAK;AAAA,MAClD;AAGA,YAAM,gBAAgB;AACtB,YAAM,kBAAkB,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,OAAO,CAAC;AAC9D,YAAM,gBAAgB;AAEtB,YAAM,cAAc,kBAAkB,MAAM,OAAO,QAAQ,eAAe,CAAC;AAC3E,YAAM,gBAAgB,kBAAkB,MAAM,OAAO,QAAQ,iBAAiB,CAAC;AAC/E,YAAM,cAAc,kBAAkB,MAAM,OAAO,QAAQ,eAAe,CAAC;AAG3E,YAAM,qBAAqB,IAAI,gBAAgB,OAAO,MAAM;AAC5D,YAAM,MAAM,mBAAmB;AAE/B,eAAS,IAAI,GAAG,IAAI,aAAa,KAAK;AACpC,cAAM,KAAK,IAAI;AACf,YAAI,EAAE,IAAI,YAAY,CAAC,KAAK;AAC5B,YAAI,KAAK,CAAC,IAAI,YAAY,CAAC,KAAK;AAChC,YAAI,KAAK,CAAC,IAAI,cAAc,CAAC,KAAK;AAClC,YAAI,KAAK,CAAC,IAAI;AAAA,MAChB;AAEA,UAAI,aAAa,oBAAoB,GAAG,CAAC;AAEzC,aAAO,OAAO,CAAC,SAAS;AACtB,YAAI,CAAC,MAAM;AACT,iBAAO,IAAI,MAAM,2BAA2B,CAAC;AAC7C;AAAA,QACF;AACA,gBAAQ,EAAE,KAAK,CAAC;AAAA,MAClB,GAAG,WAAW;AAAA,IAChB,CAAC;AAED,UAAM,iBAAiB,SAAS,MAAM;AACpC,aAAO,IAAI,MAAM,sBAAsB,CAAC;AAAA,IAC1C,CAAC;AAED,UAAM,MAAM,OAAO,SAAS,WAAW,OAAO,IAAI,gBAAgB,IAAI;AAAA,EACxE,CAAC;AACH;AAOA,SAAS,SAAS,MAAyB,OAAe,QAAgB,QAAmC;AAC3G,MAAI,UAAU,GAAG;AACf,WAAO,KAAK,MAAM;AAAA,EACpB;AAEA,QAAM,MAAM,IAAI,kBAAkB,QAAQ,MAAM;AAChD,QAAM,WAAW,IAAI,YAAY,QAAQ,MAAM;AAG/C,WAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,QAAI,SAAS;AACb,aAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC9B,YAAM,MAAM,IAAI,QAAQ;AACxB,YAAM,IAAI,KAAK,GAAG,KAAK;AACvB,gBAAU;AACV,eAAS,GAAG,IAAI,UAAU,IAAI,IAAK,SAAS,MAAM,KAAK,KAAK,IAAK;AAAA,IACnE;AAAA,EACF;AAGA,WAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,UAAM,KAAK,KAAK,IAAI,GAAG,IAAI,MAAM;AACjC,UAAM,KAAK,KAAK,IAAI,SAAS,GAAG,IAAI,MAAM;AAC1C,aAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC9B,YAAM,KAAK,KAAK,IAAI,GAAG,IAAI,MAAM;AACjC,YAAM,KAAK,KAAK,IAAI,QAAQ,GAAG,IAAI,MAAM;AAEzC,YAAM,OAAO,KAAK,QAAQ;AAC1B,YAAM,OAAO,KAAK,SAAS,KAAK;AAChC,YAAM,QAAQ,KAAK,KAAK,QAAQ;AAChC,YAAM,QAAQ,KAAK,KAAK,SAAS,KAAK;AAEtC,YAAM,IAAI,SAAS,IAAI,KAAK;AAC5B,YAAM,IAAI,KAAK,IAAK,SAAS,IAAI,KAAK,IAAK;AAC3C,YAAM,IAAI,KAAK,IAAK,SAAS,IAAI,KAAK,IAAK;AAC3C,YAAM,IAAI,KAAK,KAAK,KAAK,IAAK,SAAS,IAAI,KAAK,IAAK;AAErD,YAAM,MAAM,IAAI,IAAI,IAAI;AACxB,YAAM,QAAQ,KAAK,KAAK,MAAM,KAAK,KAAK;AACxC,UAAI,IAAI,QAAQ,CAAC,IAAI,KAAK,MAAM,MAAM,IAAI;AAAA,IAC5C;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,kBACP,MACA,OACA,QACA,QACA,QACmB;AACnB,MAAI,UAAU,KAAK,UAAU,GAAG;AAC9B,WAAO,SAAS,MAAM,OAAO,QAAQ,MAAM;AAAA,EAC7C;AAEA,MAAI,QAAQ;AACZ,MAAI,MAAyB;AAE7B,WAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,UAAM,SAAS,OAAO,OAAO,QAAQ,MAAM;AAC3C,YAAQ;AAAA,EACV;AAEA,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -15,20 +15,18 @@ import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-si
|
|
|
15
15
|
* - u_pixelRatio (float): Device pixel ratio
|
|
16
16
|
* - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)
|
|
17
17
|
* - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)
|
|
18
|
-
* - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas
|
|
19
|
-
* - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas
|
|
20
18
|
* - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)
|
|
21
|
-
* - u_scale (float): Overall zoom level of the graphics (0.
|
|
19
|
+
* - u_scale (float): Overall zoom level of the graphics (0.1 to 4)
|
|
22
20
|
* - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)
|
|
23
21
|
* - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)
|
|
24
22
|
* - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)
|
|
25
23
|
* - u_image (sampler2D): Source image texture
|
|
26
24
|
* - u_imageAspectRatio (float): Aspect ratio of the source image
|
|
27
|
-
* - u_colorFront (vec4): Foreground color in RGBA
|
|
28
|
-
* - u_colorBack (vec4): Background color in RGBA
|
|
29
|
-
* - u_colorHighlight (vec4): Secondary foreground color in RGBA (set same as colorFront for classic 2-color dithering)
|
|
25
|
+
* - u_colorFront (vec4): Foreground color in RGBA, needs originalColors off
|
|
26
|
+
* - u_colorBack (vec4): Background color in RGBA, needs originalColors off
|
|
27
|
+
* - u_colorHighlight (vec4): Secondary foreground color in RGBA (set same as colorFront for classic 2-color dithering), needs originalColors off
|
|
30
28
|
* - u_originalColors (bool): Use the original colors of the image instead of the color palette
|
|
31
|
-
* - u_inverted (bool): Inverts the image luminance, doesn't affect the color scheme
|
|
29
|
+
* - u_inverted (bool): Inverts the image luminance, doesn't affect the color scheme
|
|
32
30
|
* - u_type (float): Dithering type (1 = random, 2 = 2x2 Bayer, 3 = 4x4 Bayer, 4 = 8x8 Bayer)
|
|
33
31
|
* - u_pxSize (float): Pixel size of dithering grid (0.5 to 20)
|
|
34
32
|
* - u_colorSteps (float): Number of colors to use, applies to both color modes (1 to 7)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/shaders/image-dithering.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { proceduralHash21, declarePI } from '../shader-utils.js';\n\n/**\n * A dithering image filter with support for 4 dithering modes and multiple color palettes\n * (2-color, 3-color, and multicolor options, using either predefined colors or colors sampled\n * from the original image).\n *\n * SIZING NOTE: This shader performs sizing in the fragment shader (not vertex shader) to keep\n * u_pxSize in consistent actual pixels. The pixel grid is computed from gl_FragCoord before any\n * transforms, so scaling/rotating only affects the underlying image.\n * No vertex shader outputs (v_imageUV, v_objectUV, etc.) are used.\n *\n * Fragment shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * -
|
|
5
|
-
"mappings": ";;;;;AACA,eAAmE;AACnE,SAAS,kBAAkB,iBAAiB;
|
|
4
|
+
"sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { proceduralHash21, declarePI } from '../shader-utils.js';\n\n/**\n * A dithering image filter with support for 4 dithering modes and multiple color palettes\n * (2-color, 3-color, and multicolor options, using either predefined colors or colors sampled\n * from the original image).\n *\n * SIZING NOTE: This shader performs sizing in the fragment shader (not vertex shader) to keep\n * u_pxSize in consistent actual pixels. The pixel grid is computed from gl_FragCoord before any\n * transforms, so scaling/rotating only affects the underlying image.\n * No vertex shader outputs (v_imageUV, v_objectUV, etc.) are used.\n *\n * Fragment shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.1 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n * - u_image (sampler2D): Source image texture\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n * - u_colorFront (vec4): Foreground color in RGBA, needs originalColors off\n * - u_colorBack (vec4): Background color in RGBA, needs originalColors off\n * - u_colorHighlight (vec4): Secondary foreground color in RGBA (set same as colorFront for classic 2-color dithering), needs originalColors off\n * - u_originalColors (bool): Use the original colors of the image instead of the color palette\n * - u_inverted (bool): Inverts the image luminance, doesn't affect the color scheme\n * - u_type (float): Dithering type (1 = random, 2 = 2x2 Bayer, 3 = 4x4 Bayer, 4 = 8x8 Bayer)\n * - u_pxSize (float): Pixel size of dithering grid (0.5 to 20)\n * - u_colorSteps (float): Number of colors to use, applies to both color modes (1 to 7)\n *\n */\n\n// language=GLSL\nexport const imageDitheringFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform vec2 u_resolution;\nuniform float u_pixelRatio;\nuniform float u_originX;\nuniform float u_originY;\nuniform float u_fit;\n\nuniform float u_scale;\nuniform float u_rotation;\nuniform float u_offsetX;\nuniform float u_offsetY;\n\nuniform vec4 u_colorFront;\nuniform vec4 u_colorBack;\nuniform vec4 u_colorHighlight;\n\nuniform sampler2D u_image;\nuniform float u_imageAspectRatio;\n\nuniform float u_type;\nuniform float u_pxSize;\nuniform bool u_originalColors;\nuniform bool u_inverted;\nuniform float u_colorSteps;\n\nout vec4 fragColor;\n\n\n${ proceduralHash21 }\n${ declarePI }\n\nfloat getUvFrame(vec2 uv, vec2 pad) {\n float aa = 0.0001;\n\n float left = smoothstep(-pad.x, -pad.x + aa, uv.x);\n float right = smoothstep(1.0 + pad.x, 1.0 + pad.x - aa, uv.x);\n float bottom = smoothstep(-pad.y, -pad.y + aa, uv.y);\n float top = smoothstep(1.0 + pad.y, 1.0 + pad.y - aa, uv.y);\n\n return left * right * bottom * top;\n}\n\nvec2 getImageUV(vec2 uv) {\n vec2 boxOrigin = vec2(.5 - u_originX, u_originY - .5);\n float r = u_rotation * PI / 180.;\n mat2 graphicRotation = mat2(cos(r), sin(r), -sin(r), cos(r));\n vec2 graphicOffset = vec2(-u_offsetX, u_offsetY);\n\n vec2 imageBoxSize;\n if (u_fit == 1.) { // contain\n imageBoxSize.x = min(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio;\n } else if (u_fit == 2.) { // cover\n imageBoxSize.x = max(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio;\n } else {\n imageBoxSize.x = min(10.0, 10.0 / u_imageAspectRatio * u_imageAspectRatio);\n }\n imageBoxSize.y = imageBoxSize.x / u_imageAspectRatio;\n vec2 imageBoxScale = u_resolution.xy / imageBoxSize;\n\n vec2 imageUV = uv;\n imageUV *= imageBoxScale;\n imageUV += boxOrigin * (imageBoxScale - 1.);\n imageUV += graphicOffset;\n imageUV /= u_scale;\n imageUV.x *= u_imageAspectRatio;\n imageUV = graphicRotation * imageUV;\n imageUV.x /= u_imageAspectRatio;\n\n imageUV += .5;\n imageUV.y = 1. - imageUV.y;\n\n return imageUV;\n}\n\nconst int bayer2x2[4] = int[4](0, 2, 3, 1);\nconst int bayer4x4[16] = int[16](\n0, 8, 2, 10,\n12, 4, 14, 6,\n3, 11, 1, 9,\n15, 7, 13, 5\n);\n\nconst int bayer8x8[64] = int[64](\n0, 32, 8, 40, 2, 34, 10, 42,\n48, 16, 56, 24, 50, 18, 58, 26,\n12, 44, 4, 36, 14, 46, 6, 38,\n60, 28, 52, 20, 62, 30, 54, 22,\n3, 35, 11, 43, 1, 33, 9, 41,\n51, 19, 59, 27, 49, 17, 57, 25,\n15, 47, 7, 39, 13, 45, 5, 37,\n63, 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 float pxSize = u_pxSize * u_pixelRatio;\n vec2 pxSizeUV = gl_FragCoord.xy - .5 * u_resolution;\n pxSizeUV /= pxSize;\n vec2 canvasPixelizedUV = (floor(pxSizeUV) + .5) * pxSize;\n vec2 normalizedUV = canvasPixelizedUV / u_resolution;\n\n vec2 imageUV = getImageUV(normalizedUV);\n vec2 ditheringNoiseUV = canvasPixelizedUV;\n vec4 image = texture(u_image, imageUV);\n float frame = getUvFrame(imageUV, pxSize / u_resolution);\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 lum = u_inverted ? (1. - lum) : lum;\n\n switch (type) {\n case 1: {\n dithering = step(hash21(ditheringNoiseUV), lum);\n } break;\n case 2:\n dithering = getBayerValue(pxSizeUV, 2);\n break;\n case 3:\n dithering = getBayerValue(pxSizeUV, 4);\n break;\n default :\n dithering = getBayerValue(pxSizeUV, 8);\n break;\n }\n\n float colorSteps = max(floor(u_colorSteps), 1.);\n vec3 color = vec3(0.0);\n float opacity = 1.;\n\n dithering -= .5;\n float brightness = clamp(lum + dithering / colorSteps, 0.0, 1.0);\n brightness = mix(0.0, brightness, frame);\n brightness = mix(0.0, brightness, image.a);\n float quantLum = floor(brightness * colorSteps + 0.5) / colorSteps;\n quantLum = mix(0.0, quantLum, frame);\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 * colorSteps + 0.5) / colorSteps;\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 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 u_inverted: 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 inverted?: 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,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
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -12,22 +12,22 @@ export declare const lensDistortionMeta: {
|
|
|
12
12
|
* Fragment shader uniforms:
|
|
13
13
|
* - u_image (sampler2D): Source image texture
|
|
14
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 (-1 to 1)
|
|
16
|
-
* - u_angle (float): Direction of the spread in degrees (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) (0 to 1)
|
|
18
|
-
* - u_count (float): Number of sampled color layers along the spread; higher is smoother and costlier (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 (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 (-1 to 1)
|
|
21
|
-
* - u_dispersionColor (float): Rotates the colors around the hue wheel, 0 to 1 for a full turn
|
|
22
|
-
* - u_focusCenter (float): Reduces the spread in a circular zone at the centre; 0 keeps it full to the centre (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 (0 to 1)
|
|
24
|
-
* - u_swirl (float): Rotates the color layers around the centre by an angle growing along the spread; 0 is off (-1 to 1)
|
|
25
|
-
* - u_noise (float): Scatters the spread direction with noise; 0 is off (0 to 1)
|
|
26
|
-
* - u_noiseFrequency (float): Frequency of the noise, 0 to 1 mapped internally to 0 to 18; higher is finer
|
|
27
|
-
* - u_noiseOffset (float): Offsets the noise pattern for a different seed
|
|
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
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
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 (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
31
|
* - u_grainOverlay (float): Post-processing black/white grain overlay (0 to 1)
|
|
32
32
|
* - u_imageX (float): Pans the image horizontally behind the effect; 0 is centred (-1 to 1)
|
|
33
33
|
* - u_imageY (float): Pans the image vertically behind the effect; 0 is centred (-1 to 1)
|
|
@@ -41,7 +41,7 @@ export declare const lensDistortionMeta: {
|
|
|
41
41
|
* - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)
|
|
42
42
|
* - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)
|
|
43
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.
|
|
44
|
+
* - u_scale (float): Overall zoom level of the graphics (0.1 to 4)
|
|
45
45
|
* - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)
|
|
46
46
|
* - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)
|
|
47
47
|
* - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)
|
|
@@ -126,7 +126,7 @@ vec2 getSpread(vec2 fromCenter, float radius, vec2 warpedUV, float edgeAA, float
|
|
|
126
126
|
vec2 axis = spreadDir * (reach * strength);
|
|
127
127
|
|
|
128
128
|
if (u_noise > 0.) {
|
|
129
|
-
float turn = (valueNoise(fromCenter * u_noiseFrequency * 18. + u_noiseOffset) - .5) * 2. * u_noise;
|
|
129
|
+
float turn = (valueNoise(fromCenter * u_noiseFrequency * 18. + u_noiseOffset * 30.) - .5) * 2. * u_noise;
|
|
130
130
|
float cs = cos(turn), sn = sin(turn);
|
|
131
131
|
axis = mat2(cs, -sn, sn, cs) * axis;
|
|
132
132
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/shaders/lens-distortion.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, proceduralHash21, rotation2 } from '../shader-utils.js';\n\nexport const lensDistortionMeta = {\n maxSamples: 50,\n} as const;\n\n/**\n *\n * Lens Distortion image filter separates an image into shifting color layers\n * (recreating the chromatic aberration of a lens) and warps the image geometry,\n * curving it outward or inward like barrel and pincushion distortion.\n *\n * Fragment shader uniforms:\n * - u_image (sampler2D): Source image texture\n * - u_spread (float): Strength of the color split; how far the color layers are pushed apart; 0 is off (0 to 1)\n * - u_bias (float): Shifts the colors toward one end of the spread; 0 spaces them evenly (-1 to 1)\n * - u_angle (float): Direction of the spread in degrees (0 to 360)\n * - u_perspective (float): Shapes the spread direction from a straight line (0) to a radial burst out from the centre (1) (0 to 1)\n * - u_count (float): Number of sampled color layers along the spread; higher is smoother and costlier (2 to 50)\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 (0 to 1)\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 (-1 to 1)\n * - u_dispersionColor (float): Rotates the colors around the hue wheel, 0 to 1 for a full turn\n * - u_focusCenter (float): Reduces the spread in a circular zone at the centre; 0 keeps it full to the centre (0 to 1)\n * - u_focusEdges (float): Reduces the spread toward the edges; 0 keeps it full to the edges, 1 restores the original image there (0 to 1)\n * - u_swirl (float): Rotates the color layers around the centre by an angle growing along the spread; 0 is off (-1 to 1)\n * - u_noise (float): Scatters the spread direction with noise; 0 is off (0 to 1)\n * - u_noiseFrequency (float): Frequency of the noise, 0 to 1 mapped internally to 0 to 18; higher is finer (no effect with noise = 0)\n * - u_noiseOffset (float): Offsets the noise pattern for a different seed (no effect with noise = 0)\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)\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)\n * - u_grainMixer (float): Scatters the spread with grain noise, breaking up the edges of the color layers (0 to 1)\n * - u_grainOverlay (float): Post-processing black/white grain overlay (0 to 1)\n * - u_imageX (float): Pans the image horizontally behind the effect; 0 is centred (-1 to 1)\n * - u_imageY (float): Pans the image vertically behind the effect; 0 is centred (-1 to 1)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_imageUV (vec2): Image UV coordinates with global sizing (rotation, scale, offset, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n *\n */\n\n// language=GLSL\nexport const lensDistortionFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform sampler2D u_image;\nuniform float u_imageAspectRatio;\nuniform float u_spread;\nuniform float u_bias;\nuniform float u_angle;\nuniform float u_perspective;\nuniform float u_count;\nuniform float u_dispersion;\nuniform float u_dispersionShift;\nuniform float u_dispersionColor;\nuniform float u_focusCenter;\nuniform float u_focusEdges;\nuniform float u_swirl;\nuniform float u_noise;\nuniform float u_noiseFrequency;\nuniform float u_noiseOffset;\nuniform float u_lensBulge;\nuniform float u_lensCircle;\nuniform float u_grainMixer;\nuniform float u_grainOverlay;\nuniform float u_imageX;\nuniform float u_imageY;\n\nin vec2 v_imageUV;\n\nout vec4 fragColor;\n\n${declarePI}\n${proceduralHash21}\n${rotation2}\n\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = hash21(i);\n float b = hash21(i + vec2(1., 0.));\n float c = hash21(i + vec2(0., 1.));\n float d = hash21(i + vec2(1., 1.));\n vec2 u = f * f * (3. - 2. * f);\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfloat getUvFrame(vec2 uv) {\n vec2 invAA = 1. / clamp(fwidth(uv), 1e-5, .02);\n vec2 lo = clamp(uv * invAA + .5, 0., 1.);\n vec2 hi = clamp((1. - uv) * invAA + .5, 0., 1.);\n return lo.x * hi.x * lo.y * hi.y;\n}\n\nvec4 sampleOverWhite(vec2 uv) {\n vec4 img = texture(u_image, uv);\n float cover = img.a * getUvFrame(uv);\n vec3 colorOverWhite = mix(vec3(1.), img.rgb, cover);\n return vec4(colorOverWhite, cover);\n}\n\nvec3 hueColor(float hue) {\n vec3 rgb = clamp(abs(mod(hue * 6. + vec3(0., 4., 2.), 6.) - 3.) - 1., 0., 1.);\n rgb = rgb * rgb * (3. - 2. * rgb);\n return rgb;\n}\n \nfloat boxInradius() {\n return .5 * min(u_imageAspectRatio, 1.);\n}\n\nfloat boxOutradius() {\n return .5 * length(vec2(u_imageAspectRatio, 1.));\n}\n\nfloat spreadReach() {\n return .7 * pow(u_spread, 1.3 + 2.7 * u_spread);\n}\n\nfloat innerCircleMask(float radius, float inradius) {\n return 1. - smoothstep(.5 * inradius, 1.1 * inradius, radius);\n}\n\nfloat dispersionCurve(float t, float biasPow) {\n float mirroredT = u_bias < 0. ? 1. - t : t;\n float curved = pow(mirroredT, biasPow);\n return u_bias < 0. ? 1. - curved : curved;\n}\n\nvec2 getSpread(vec2 fromCenter, float radius, vec2 warpedUV, float edgeAA, float inradius, float outradius, float reach, out float outStrength) {\n float angleRad = radians(u_angle);\n vec2 uniformDir = vec2(cos(angleRad), sin(angleRad));\n\n float invInradius = 1. / inradius;\n vec2 radialDir = fromCenter * invInradius;\n float maxLen = (outradius + reach) * invInradius;\n float radialLen = radius * invInradius;\n if (radialLen > maxLen) radialDir *= maxLen / radialLen;\n vec2 spreadDir = mix(uniformDir, radialDir, u_perspective);\n\n float bandProximity = smoothstep(inradius * .8, inradius, radius);\n float lensCircleMaxing = bandProximity * u_lensCircle * u_lensCircle * u_lensCircle;\n spreadDir = mix(spreadDir, radialDir, lensCircleMaxing);\n\n vec2 warpedAbs = abs(warpedUV - .5);\n float inner = mix(1., smoothstep(0., mix(inradius, outradius, u_focusCenter), radius), u_focusCenter);\n float boxDist = max(warpedAbs.x, warpedAbs.y) * 2.;\n float outer = mix(1., 1. - min(boxDist, 1.), u_focusEdges);\n float strength = inner * outer;\n\n strength *= mix(1., mix(.15, .03, max(-u_lensBulge, 0.)), lensCircleMaxing);\n\n vec2 outside = max(warpedAbs - .5, 0.);\n float margin = max(reach * length(spreadDir) * (1. - u_lensCircle), edgeAA);\n strength *= 1. - smoothstep(0., margin, length(outside));\n\n outStrength = strength;\n vec2 axis = spreadDir * (reach * strength);\n\n if (u_noise > 0.) {\n float turn = (valueNoise(fromCenter * u_noiseFrequency * 18. + u_noiseOffset) - .5) * 2. * u_noise;\n float cs = cos(turn), sn = sin(turn);\n axis = mat2(cs, -sn, sn, cs) * axis;\n }\n\n axis.x /= u_imageAspectRatio;\n return axis;\n}\n\nvec2 lensWarp(vec2 fromCenter, float radius, float inradius, out float bulgeFade) {\n bulgeFade = 1.;\n if (u_lensBulge == 0. && u_lensCircle <= 0.) return fromCenter;\n if (radius < 1e-5) return fromCenter;\n \n float r = radius;\n\n if (u_lensBulge != 0.) {\n float rn = radius / inradius;\n float bulge = abs(u_lensBulge) * (u_lensBulge > 0. ? 1.4 : 1.2);\n if (u_lensBulge > 0.) bulgeFade = 1. - smoothstep(1.45, 1.53, rn * bulge);\n float map = u_lensBulge > 0.\n ? tan(min(rn * bulge, 1.53)) / tan(bulge)\n : atan(rn * tan(bulge)) / bulge;\n float bulgeScale = map / rn;\n fromCenter *= bulgeScale;\n r *= bulgeScale;\n }\n\n if (u_lensCircle > 0.) {\n vec2 dir = fromCenter / max(r, 1e-5);\n vec2 halfBox = vec2(u_imageAspectRatio, 1.) * .5;\n float rBox = min(halfBox.x / max(abs(dir.x), 1e-4), halfBox.y / max(abs(dir.y), 1e-4));\n float band = inradius * mix(.03, .2, .5 * (u_lensBulge + 1.));\n float innerEdge = inradius - band;\n float over = smoothstep(0., 1., (r - innerEdge) / band);\n float g = r + (rBox - inradius) * pow(over, 14.);\n fromCenter = dir * mix(r, g, u_lensCircle);\n }\n\n return fromCenter;\n}\n\nvoid main() {\n vec2 uv = v_imageUV;\n\n float invAspect = 1. / u_imageAspectRatio;\n float inradius = boxInradius();\n float outradius = boxOutradius();\n float reach = spreadReach();\n\n vec2 fromCenter = uv - .5;\n fromCenter.x *= u_imageAspectRatio;\n float radius = length(fromCenter);\n\n float bulgeFade;\n vec2 warpedFromCenter = lensWarp(fromCenter, radius, inradius, bulgeFade);\n vec2 baseUV = vec2(warpedFromCenter.x * invAspect, warpedFromCenter.y) + .5;\n\n vec2 baseDerivative = fwidth(baseUV);\n float edgeAA = clamp(2. * max(baseDerivative.x, baseDerivative.y), .001, .02);\n\n float spreadStrength;\n vec2 spreadAxis = getSpread(fromCenter, radius, baseUV, edgeAA, inradius, outradius, reach, spreadStrength);\n\n vec2 grainUV = vec2(0.);\n if (u_grainMixer > 0. || u_grainOverlay > 0.) {\n vec2 dudx = dFdx(v_imageUV);\n vec2 dudy = dFdy(v_imageUV);\n grainUV = (v_imageUV - .5) * (.8 / vec2(length(dudx), length(dudy))) + .5;\n }\n\n if (u_grainMixer > 0.) {\n float grainSeed = valueNoise(grainUV);\n vec2 jit = fract(grainSeed * vec2(157.31, 113.57)) * 2. - 1.;\n spreadAxis += jit * .3 * u_grainMixer * length(spreadAxis);\n }\n\n float count = floor(u_count);\n int countInteger = int(count);\n\n float invCount = 1. / count;\n float invSpan = 1. / max(count - 1., 1.);\n float hueBase = u_dispersionColor + .5;\n float biasPower = 1. + 2. * abs(u_bias);\n vec2 tapBias = .5 - vec2(u_imageX, u_imageY);\n\n float centerAmount = clamp(1. - u_dispersionShift, 0., 1.);\n float edgesAmount = clamp(1. + u_dispersionShift, 0., 1.);\n float dispersion = u_dispersion * mix(edgesAmount, centerAmount, innerCircleMask(radius, inradius));\n float dispersionPower = pow(dispersion, .8);\n\n float warpedRadius = length(warpedFromCenter);\n float swirlAngle = u_swirl * .4 * PI * spreadStrength * u_spread * min(1., inradius / max(warpedRadius, 1e-4));\n\n vec3 colorSum = vec3(0.);\n vec3 weightSum = vec3(0.);\n float coverSum = 0.;\n\n for (int i = 0; i < ${lensDistortionMeta.maxSamples}; i++) {\n if (i >= countInteger) break;\n\n float layer = float(i);\n float hue = hueBase + layer * invCount;\n\n float spread = layer * invSpan;\n if (u_bias != 0.) spread = dispersionCurve(spread, biasPower);\n\n // mix(axis, -axis, spread) is axis scaled by the signed position along the fan\n float fanPos = 1. - 2. * spread;\n vec2 tapUV = baseUV + spreadAxis * fanPos - .5;\n\n if (u_swirl != 0.) {\n tapUV.x *= u_imageAspectRatio;\n tapUV = rotate(tapUV, swirlAngle * fanPos);\n tapUV.x *= invAspect;\n }\n\n vec4 tap = sampleOverWhite(tapUV + tapBias);\n vec3 weight = 1. - dispersionPower * hueColor(hue);\n\n colorSum += tap.rgb * weight;\n weightSum += weight;\n coverSum += tap.a;\n }\n\n vec3 color = colorSum / max(weightSum, 1e-4);\n float coverAvg = coverSum * invCount;\n\n float ground = min(color.r, min(color.g, color.b));\n float alpha = max(coverAvg, 1. - ground);\n vec3 premult = max(color - (1. - alpha), 0.);\n fragColor = vec4(premult, alpha) * bulgeFade;\n\n if (u_grainOverlay > 0.) {\n float grain = valueNoise(rotate(grainUV, 1.) + vec2(3.));\n grain = mix(grain, valueNoise(rotate(grainUV, 2.) + vec2(-1.)), .5);\n grain = pow(grain, 1.3);\n float grainV = grain * 2. - 1.;\n float grainStrength = pow(u_grainOverlay * abs(grainV), .8) * fragColor.a;\n fragColor.rgb = mix(fragColor.rgb, vec3(step(0., grainV)) * fragColor.a, .35 * grainStrength);\n }\n}\n`;\n\nexport interface LensDistortionUniforms extends ShaderSizingUniforms {\n u_image: HTMLImageElement | string | undefined;\n u_spread: number;\n u_bias: number;\n u_angle: number;\n u_perspective: number;\n u_count: number;\n u_dispersion: number;\n u_dispersionShift: number;\n u_dispersionColor: number;\n u_focusCenter: number;\n u_focusEdges: number;\n u_swirl: number;\n u_noise: number;\n u_noiseFrequency: number;\n u_noiseOffset: number;\n u_lensBulge: number;\n u_lensCircle: number;\n u_grainMixer: number;\n u_grainOverlay: number;\n u_imageX: number;\n u_imageY: number;\n}\n\nexport interface LensDistortionParams extends ShaderSizingParams, ShaderMotionParams {\n image?: HTMLImageElement | string;\n spread?: number;\n bias?: number;\n angle?: number;\n perspective?: number;\n count?: number;\n dispersion?: number;\n dispersionShift?: number;\n dispersionColor?: number;\n focusCenter?: number;\n focusEdges?: number;\n swirl?: number;\n noise?: number;\n noiseFrequency?: number;\n noiseOffset?: number;\n lensBulge?: number;\n lensCircle?: number;\n grainMixer?: number;\n grainOverlay?: number;\n imageX?: number;\n imageY?: number;\n}\n"],
|
|
4
|
+
"sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, proceduralHash21, rotation2 } from '../shader-utils.js';\n\nexport const lensDistortionMeta = {\n maxSamples: 50,\n} as const;\n\n/**\n *\n * Lens Distortion image filter separates an image into shifting color layers\n * (recreating the chromatic aberration of a lens) and warps the image geometry,\n * curving it outward or inward like barrel and pincushion distortion.\n *\n * Fragment shader uniforms:\n * - u_image (sampler2D): Source image texture\n * - u_spread (float): Strength of the color split; how far the color layers are pushed apart; 0 is off (0 to 1)\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)\n * - u_angle (float): Direction of the spread in degrees, needs spread > 0, perspective < 1 (0 to 360)\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)\n * - u_count (float): Number of sampled color layers along the spread; higher is smoother and costlier, needs spread > 0 (2 to 50)\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)\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)\n * - u_dispersionColor (float): Rotates the colors around the hue wheel, 0 to 1 for a full turn, needs spread > 0, dispersion > 0\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)\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)\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)\n * - u_noise (float): Scatters the spread direction with noise; 0 is off, needs spread > 0 (0 to 1)\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\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\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)\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)\n * - u_grainMixer (float): Scatters the spread with grain noise, breaking up the edges of the color layers, needs spread > 0 (0 to 1)\n * - u_grainOverlay (float): Post-processing black/white grain overlay (0 to 1)\n * - u_imageX (float): Pans the image horizontally behind the effect; 0 is centred (-1 to 1)\n * - u_imageY (float): Pans the image vertically behind the effect; 0 is centred (-1 to 1)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_imageUV (vec2): Image UV coordinates with global sizing (rotation, scale, offset, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.1 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n *\n */\n\n// language=GLSL\nexport const lensDistortionFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform sampler2D u_image;\nuniform float u_imageAspectRatio;\nuniform float u_spread;\nuniform float u_bias;\nuniform float u_angle;\nuniform float u_perspective;\nuniform float u_count;\nuniform float u_dispersion;\nuniform float u_dispersionShift;\nuniform float u_dispersionColor;\nuniform float u_focusCenter;\nuniform float u_focusEdges;\nuniform float u_swirl;\nuniform float u_noise;\nuniform float u_noiseFrequency;\nuniform float u_noiseOffset;\nuniform float u_lensBulge;\nuniform float u_lensCircle;\nuniform float u_grainMixer;\nuniform float u_grainOverlay;\nuniform float u_imageX;\nuniform float u_imageY;\n\nin vec2 v_imageUV;\n\nout vec4 fragColor;\n\n${declarePI}\n${proceduralHash21}\n${rotation2}\n\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = hash21(i);\n float b = hash21(i + vec2(1., 0.));\n float c = hash21(i + vec2(0., 1.));\n float d = hash21(i + vec2(1., 1.));\n vec2 u = f * f * (3. - 2. * f);\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfloat getUvFrame(vec2 uv) {\n vec2 invAA = 1. / clamp(fwidth(uv), 1e-5, .02);\n vec2 lo = clamp(uv * invAA + .5, 0., 1.);\n vec2 hi = clamp((1. - uv) * invAA + .5, 0., 1.);\n return lo.x * hi.x * lo.y * hi.y;\n}\n\nvec4 sampleOverWhite(vec2 uv) {\n vec4 img = texture(u_image, uv);\n float cover = img.a * getUvFrame(uv);\n vec3 colorOverWhite = mix(vec3(1.), img.rgb, cover);\n return vec4(colorOverWhite, cover);\n}\n\nvec3 hueColor(float hue) {\n vec3 rgb = clamp(abs(mod(hue * 6. + vec3(0., 4., 2.), 6.) - 3.) - 1., 0., 1.);\n rgb = rgb * rgb * (3. - 2. * rgb);\n return rgb;\n}\n \nfloat boxInradius() {\n return .5 * min(u_imageAspectRatio, 1.);\n}\n\nfloat boxOutradius() {\n return .5 * length(vec2(u_imageAspectRatio, 1.));\n}\n\nfloat spreadReach() {\n return .7 * pow(u_spread, 1.3 + 2.7 * u_spread);\n}\n\nfloat innerCircleMask(float radius, float inradius) {\n return 1. - smoothstep(.5 * inradius, 1.1 * inradius, radius);\n}\n\nfloat dispersionCurve(float t, float biasPow) {\n float mirroredT = u_bias < 0. ? 1. - t : t;\n float curved = pow(mirroredT, biasPow);\n return u_bias < 0. ? 1. - curved : curved;\n}\n\nvec2 getSpread(vec2 fromCenter, float radius, vec2 warpedUV, float edgeAA, float inradius, float outradius, float reach, out float outStrength) {\n float angleRad = radians(u_angle);\n vec2 uniformDir = vec2(cos(angleRad), sin(angleRad));\n\n float invInradius = 1. / inradius;\n vec2 radialDir = fromCenter * invInradius;\n float maxLen = (outradius + reach) * invInradius;\n float radialLen = radius * invInradius;\n if (radialLen > maxLen) radialDir *= maxLen / radialLen;\n vec2 spreadDir = mix(uniformDir, radialDir, u_perspective);\n\n float bandProximity = smoothstep(inradius * .8, inradius, radius);\n float lensCircleMaxing = bandProximity * u_lensCircle * u_lensCircle * u_lensCircle;\n spreadDir = mix(spreadDir, radialDir, lensCircleMaxing);\n\n vec2 warpedAbs = abs(warpedUV - .5);\n float inner = mix(1., smoothstep(0., mix(inradius, outradius, u_focusCenter), radius), u_focusCenter);\n float boxDist = max(warpedAbs.x, warpedAbs.y) * 2.;\n float outer = mix(1., 1. - min(boxDist, 1.), u_focusEdges);\n float strength = inner * outer;\n\n strength *= mix(1., mix(.15, .03, max(-u_lensBulge, 0.)), lensCircleMaxing);\n\n vec2 outside = max(warpedAbs - .5, 0.);\n float margin = max(reach * length(spreadDir) * (1. - u_lensCircle), edgeAA);\n strength *= 1. - smoothstep(0., margin, length(outside));\n\n outStrength = strength;\n vec2 axis = spreadDir * (reach * strength);\n\n if (u_noise > 0.) {\n float turn = (valueNoise(fromCenter * u_noiseFrequency * 18. + u_noiseOffset * 30.) - .5) * 2. * u_noise;\n float cs = cos(turn), sn = sin(turn);\n axis = mat2(cs, -sn, sn, cs) * axis;\n }\n\n axis.x /= u_imageAspectRatio;\n return axis;\n}\n\nvec2 lensWarp(vec2 fromCenter, float radius, float inradius, out float bulgeFade) {\n bulgeFade = 1.;\n if (u_lensBulge == 0. && u_lensCircle <= 0.) return fromCenter;\n if (radius < 1e-5) return fromCenter;\n \n float r = radius;\n\n if (u_lensBulge != 0.) {\n float rn = radius / inradius;\n float bulge = abs(u_lensBulge) * (u_lensBulge > 0. ? 1.4 : 1.2);\n if (u_lensBulge > 0.) bulgeFade = 1. - smoothstep(1.45, 1.53, rn * bulge);\n float map = u_lensBulge > 0.\n ? tan(min(rn * bulge, 1.53)) / tan(bulge)\n : atan(rn * tan(bulge)) / bulge;\n float bulgeScale = map / rn;\n fromCenter *= bulgeScale;\n r *= bulgeScale;\n }\n\n if (u_lensCircle > 0.) {\n vec2 dir = fromCenter / max(r, 1e-5);\n vec2 halfBox = vec2(u_imageAspectRatio, 1.) * .5;\n float rBox = min(halfBox.x / max(abs(dir.x), 1e-4), halfBox.y / max(abs(dir.y), 1e-4));\n float band = inradius * mix(.03, .2, .5 * (u_lensBulge + 1.));\n float innerEdge = inradius - band;\n float over = smoothstep(0., 1., (r - innerEdge) / band);\n float g = r + (rBox - inradius) * pow(over, 14.);\n fromCenter = dir * mix(r, g, u_lensCircle);\n }\n\n return fromCenter;\n}\n\nvoid main() {\n vec2 uv = v_imageUV;\n\n float invAspect = 1. / u_imageAspectRatio;\n float inradius = boxInradius();\n float outradius = boxOutradius();\n float reach = spreadReach();\n\n vec2 fromCenter = uv - .5;\n fromCenter.x *= u_imageAspectRatio;\n float radius = length(fromCenter);\n\n float bulgeFade;\n vec2 warpedFromCenter = lensWarp(fromCenter, radius, inradius, bulgeFade);\n vec2 baseUV = vec2(warpedFromCenter.x * invAspect, warpedFromCenter.y) + .5;\n\n vec2 baseDerivative = fwidth(baseUV);\n float edgeAA = clamp(2. * max(baseDerivative.x, baseDerivative.y), .001, .02);\n\n float spreadStrength;\n vec2 spreadAxis = getSpread(fromCenter, radius, baseUV, edgeAA, inradius, outradius, reach, spreadStrength);\n\n vec2 grainUV = vec2(0.);\n if (u_grainMixer > 0. || u_grainOverlay > 0.) {\n vec2 dudx = dFdx(v_imageUV);\n vec2 dudy = dFdy(v_imageUV);\n grainUV = (v_imageUV - .5) * (.8 / vec2(length(dudx), length(dudy))) + .5;\n }\n\n if (u_grainMixer > 0.) {\n float grainSeed = valueNoise(grainUV);\n vec2 jit = fract(grainSeed * vec2(157.31, 113.57)) * 2. - 1.;\n spreadAxis += jit * .3 * u_grainMixer * length(spreadAxis);\n }\n\n float count = floor(u_count);\n int countInteger = int(count);\n\n float invCount = 1. / count;\n float invSpan = 1. / max(count - 1., 1.);\n float hueBase = u_dispersionColor + .5;\n float biasPower = 1. + 2. * abs(u_bias);\n vec2 tapBias = .5 - vec2(u_imageX, u_imageY);\n\n float centerAmount = clamp(1. - u_dispersionShift, 0., 1.);\n float edgesAmount = clamp(1. + u_dispersionShift, 0., 1.);\n float dispersion = u_dispersion * mix(edgesAmount, centerAmount, innerCircleMask(radius, inradius));\n float dispersionPower = pow(dispersion, .8);\n\n float warpedRadius = length(warpedFromCenter);\n float swirlAngle = u_swirl * .4 * PI * spreadStrength * u_spread * min(1., inradius / max(warpedRadius, 1e-4));\n\n vec3 colorSum = vec3(0.);\n vec3 weightSum = vec3(0.);\n float coverSum = 0.;\n\n for (int i = 0; i < ${lensDistortionMeta.maxSamples}; i++) {\n if (i >= countInteger) break;\n\n float layer = float(i);\n float hue = hueBase + layer * invCount;\n\n float spread = layer * invSpan;\n if (u_bias != 0.) spread = dispersionCurve(spread, biasPower);\n\n // mix(axis, -axis, spread) is axis scaled by the signed position along the fan\n float fanPos = 1. - 2. * spread;\n vec2 tapUV = baseUV + spreadAxis * fanPos - .5;\n\n if (u_swirl != 0.) {\n tapUV.x *= u_imageAspectRatio;\n tapUV = rotate(tapUV, swirlAngle * fanPos);\n tapUV.x *= invAspect;\n }\n\n vec4 tap = sampleOverWhite(tapUV + tapBias);\n vec3 weight = 1. - dispersionPower * hueColor(hue);\n\n colorSum += tap.rgb * weight;\n weightSum += weight;\n coverSum += tap.a;\n }\n\n vec3 color = colorSum / max(weightSum, 1e-4);\n float coverAvg = coverSum * invCount;\n\n float ground = min(color.r, min(color.g, color.b));\n float alpha = max(coverAvg, 1. - ground);\n vec3 premult = max(color - (1. - alpha), 0.);\n fragColor = vec4(premult, alpha) * bulgeFade;\n\n if (u_grainOverlay > 0.) {\n float grain = valueNoise(rotate(grainUV, 1.) + vec2(3.));\n grain = mix(grain, valueNoise(rotate(grainUV, 2.) + vec2(-1.)), .5);\n grain = pow(grain, 1.3);\n float grainV = grain * 2. - 1.;\n float grainStrength = pow(u_grainOverlay * abs(grainV), .8) * fragColor.a;\n fragColor.rgb = mix(fragColor.rgb, vec3(step(0., grainV)) * fragColor.a, .35 * grainStrength);\n }\n}\n`;\n\nexport interface LensDistortionUniforms extends ShaderSizingUniforms {\n u_image: HTMLImageElement | string | undefined;\n u_spread: number;\n u_bias: number;\n u_angle: number;\n u_perspective: number;\n u_count: number;\n u_dispersion: number;\n u_dispersionShift: number;\n u_dispersionColor: number;\n u_focusCenter: number;\n u_focusEdges: number;\n u_swirl: number;\n u_noise: number;\n u_noiseFrequency: number;\n u_noiseOffset: number;\n u_lensBulge: number;\n u_lensCircle: number;\n u_grainMixer: number;\n u_grainOverlay: number;\n u_imageX: number;\n u_imageY: number;\n}\n\nexport interface LensDistortionParams extends ShaderSizingParams, ShaderMotionParams {\n image?: HTMLImageElement | string;\n spread?: number;\n bias?: number;\n angle?: number;\n perspective?: number;\n count?: number;\n dispersion?: number;\n dispersionShift?: number;\n dispersionColor?: number;\n focusCenter?: number;\n focusEdges?: number;\n swirl?: number;\n noise?: number;\n noiseFrequency?: number;\n noiseOffset?: number;\n lensBulge?: number;\n lensCircle?: number;\n grainMixer?: number;\n grainOverlay?: number;\n imageX?: number;\n imageY?: number;\n}\n"],
|
|
5
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
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -9,7 +9,6 @@ import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-si
|
|
|
9
9
|
* - u_time (float): Animation time
|
|
10
10
|
* - u_resolution (vec2): Canvas resolution in pixels
|
|
11
11
|
* - u_image (sampler2D): Pre-processed source image texture (R = edge gradient, G = opacity)
|
|
12
|
-
* - u_imageAspectRatio (float): Aspect ratio of the source image
|
|
13
12
|
* - u_colorBack (vec4): Background color in RGBA
|
|
14
13
|
* - u_colorTint (vec4): Overlay color in RGBA (color burn blending used)
|
|
15
14
|
* - u_repetition (float): Density of pattern stripes (1 to 10)
|
|
@@ -19,7 +18,7 @@ import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-si
|
|
|
19
18
|
* - u_distortion (float): Noise distortion over the stripes pattern (0 to 1)
|
|
20
19
|
* - u_contour (float): Strength of the distortion on the shape edges (0 to 1)
|
|
21
20
|
* - u_angle (float): Direction of pattern animation in degrees (0 to 360)
|
|
22
|
-
* - u_shape (float): Predefined shape
|
|
21
|
+
* - u_shape (float): Predefined shape mask, needs no image (0 = none, 1 = circle, 2 = daisy, 3 = diamond, 4 = metaballs)
|
|
23
22
|
* - u_isImage (bool): Whether an image is being used as the effect mask
|
|
24
23
|
*
|
|
25
24
|
* Vertex shader outputs (used in fragment shader):
|
|
@@ -36,7 +35,7 @@ import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-si
|
|
|
36
35
|
* - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas
|
|
37
36
|
* - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas
|
|
38
37
|
* - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)
|
|
39
|
-
* - u_scale (float): Overall zoom level of the graphics (0.
|
|
38
|
+
* - u_scale (float): Overall zoom level of the graphics (0.1 to 4)
|
|
40
39
|
* - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)
|
|
41
40
|
* - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)
|
|
42
41
|
* - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)
|
|
@@ -9,7 +9,6 @@ const liquidMetalFragmentShader = `#version 300 es
|
|
|
9
9
|
precision mediump float;
|
|
10
10
|
|
|
11
11
|
uniform sampler2D u_image;
|
|
12
|
-
uniform float u_imageAspectRatio;
|
|
13
12
|
|
|
14
13
|
uniform vec2 u_resolution;
|
|
15
14
|
uniform float u_time;
|
|
@@ -129,6 +128,9 @@ void main() {
|
|
|
129
128
|
rotatedUV.x * sinA + rotatedUV.y * cosA
|
|
130
129
|
) + vec2(.5);
|
|
131
130
|
|
|
131
|
+
// u_contour is applied in 2 separate ranges:
|
|
132
|
+
// - 0 to .4 sets the edge hardness, saturated above .4 (both branches below)
|
|
133
|
+
// - .5 to 1 warps the stripes direction along the edges, inactive below .5 (see u_contour range 2)
|
|
132
134
|
if (u_isImage == true) {
|
|
133
135
|
float edgeRaw = img.r;
|
|
134
136
|
edge = blurEdge3x3(u_image, uv, dudx, dudy, 6., edgeRaw);
|
|
@@ -261,6 +263,7 @@ void main() {
|
|
|
261
263
|
direction += diagBLtoTR;
|
|
262
264
|
float contour = 0.;
|
|
263
265
|
direction -= 2. * noise * diagBLtoTR * (smoothstep(0., 1., edge) * (1.0 - smoothstep(0., 1., edge)));
|
|
266
|
+
// u_contour range 2
|
|
264
267
|
direction *= mix(1., 1. - edge, smoothstep(.5, 1., u_contour));
|
|
265
268
|
direction -= 1.7 * edge * smoothstep(.5, 1., u_contour);
|
|
266
269
|
direction += .2 * pow(u_contour, 4.) * (1.0 - smoothstep(0., 1., edge));
|