@paper-design/shaders 0.0.60 → 0.0.62

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/shader-mount.d.ts +5 -1
  2. package/dist/shader-mount.js +5 -2
  3. package/dist/shader-mount.js.map +2 -2
  4. package/dist/shaders/color-panels.js +4 -3
  5. package/dist/shaders/color-panels.js.map +2 -2
  6. package/dist/shaders/dithering.js +4 -4
  7. package/dist/shaders/dithering.js.map +1 -1
  8. package/dist/shaders/dot-grid.js +5 -4
  9. package/dist/shaders/dot-grid.js.map +2 -2
  10. package/dist/shaders/dot-orbit.js +1 -1
  11. package/dist/shaders/dot-orbit.js.map +1 -1
  12. package/dist/shaders/fluted-glass.d.ts +19 -5
  13. package/dist/shaders/fluted-glass.js +250 -64
  14. package/dist/shaders/fluted-glass.js.map +2 -2
  15. package/dist/shaders/god-rays.js +4 -2
  16. package/dist/shaders/god-rays.js.map +2 -2
  17. package/dist/shaders/grain-gradient.js +4 -4
  18. package/dist/shaders/grain-gradient.js.map +1 -1
  19. package/dist/shaders/heatmap.js +13 -12
  20. package/dist/shaders/heatmap.js.map +2 -2
  21. package/dist/shaders/image-dithering.js +2 -2
  22. package/dist/shaders/image-dithering.js.map +1 -1
  23. package/dist/shaders/liquid-metal.js +2 -2
  24. package/dist/shaders/liquid-metal.js.map +1 -1
  25. package/dist/shaders/mesh-gradient.js +4 -4
  26. package/dist/shaders/mesh-gradient.js.map +1 -1
  27. package/dist/shaders/neuro-noise.js +1 -1
  28. package/dist/shaders/neuro-noise.js.map +1 -1
  29. package/dist/shaders/paper-texture.js +4 -4
  30. package/dist/shaders/paper-texture.js.map +1 -1
  31. package/dist/shaders/perlin-noise.js +2 -2
  32. package/dist/shaders/perlin-noise.js.map +1 -1
  33. package/dist/shaders/pulsing-border.js +7 -7
  34. package/dist/shaders/pulsing-border.js.map +1 -1
  35. package/dist/shaders/smoke-ring.js +8 -6
  36. package/dist/shaders/smoke-ring.js.map +2 -2
  37. package/dist/shaders/spiral.js +1 -1
  38. package/dist/shaders/spiral.js.map +1 -1
  39. package/dist/shaders/static-mesh-gradient.js +2 -2
  40. package/dist/shaders/static-mesh-gradient.js.map +1 -1
  41. package/dist/shaders/static-radial-gradient.js +17 -14
  42. package/dist/shaders/static-radial-gradient.js.map +2 -2
  43. package/dist/shaders/swirl.js +2 -1
  44. package/dist/shaders/swirl.js.map +2 -2
  45. package/dist/shaders/warp.js +4 -2
  46. package/dist/shaders/warp.js.map +2 -2
  47. package/dist/shaders/water.js +5 -5
  48. package/dist/shaders/water.js.map +1 -1
  49. package/dist/shaders/waves.js +3 -1
  50. package/dist/shaders/waves.js.map +2 -2
  51. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/shaders/perlin-noise.ts"],
4
- "sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, colorBandingFix } from '../shader-utils.js';\n\n/**\n * 3d Perlin noise; original algorithm: https://www.shadertoy.com/view/NlSGDz\n *\n * Uniforms:\n * - u_colorBack, u_colorFront (RGBA)\n * - u_proportion: (0..1) blend point between 2 colors (0.5 = equal distribution)\n * - u_softness: color transition sharpness (0 = hard edge, 1 = smooth fade)\n * - u_octaveCount: more octaves => more detailed pattern\n * - u_persistence: roughness, falloff between octaves\n * - u_lacunarity: frequency step, typically around 2, defines how compressed is the pattern\n *\n */\n\n// language=GLSL\nexport const perlinNoiseFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform vec4 u_colorFront;\nuniform vec4 u_colorBack;\nuniform float u_proportion;\nuniform float u_softness;\nuniform float u_octaveCount;\nuniform float u_persistence;\nuniform float u_lacunarity;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n\nfloat hash11(float p) {\n p = fract(p * 0.3183099) + 0.1;\n p *= p + 19.19;\n return fract(p * p);\n}\n\nfloat hash21(vec2 p) {\n p = fract(p * vec2(0.3183099, 0.3678794)) + 0.1;\n p += dot(p, p + 19.19);\n return fract(p.x * p.y);\n}\n\nfloat hash31(vec3 p) {\n p = fract(p * 0.3183099) + 0.1;\n p += dot(p, p.yzx + 19.19);\n return fract(p.x * (p.y + p.z));\n}\n\nvec3 hash33(vec3 p) {\n p = fract(p * 0.3183099) + 0.1;\n p += dot(p, p.yzx + 19.19);\n return fract(vec3(p.x * p.y, p.y * p.z, p.z * p.x));\n}\n\nvec3 gradientSafe(vec3 p) {\n vec3 h = hash33(p) * 2.0 - 1.;\n return normalize(h + 0.001);\n}\n\nvec3 gradientPredefined(float hash) {\n int idx = int(hash * 12.0) % 12;\n\n if (idx == 0) return vec3(1, 1, 0);\n if (idx == 1) return vec3(-1, 1, 0);\n if (idx == 2) return vec3(1, -1, 0);\n if (idx == 3) return vec3(-1, -1, 0);\n if (idx == 4) return vec3(1, 0, 1);\n if (idx == 5) return vec3(-1, 0, 1);\n if (idx == 6) return vec3(1, 0, -1);\n if (idx == 7) return vec3(-1, 0, -1);\n if (idx == 8) return vec3(0, 1, 1);\n if (idx == 9) return vec3(0, -1, 1);\n if (idx == 10) return vec3(0, 1, -1);\n return vec3(0, -1, -1);// idx == 11\n}\n\nfloat interpolateSafe(float v000, float v001, float v010, float v011,\nfloat v100, float v101, float v110, float v111, vec3 t) {\n t = clamp(t, 0.0, 1.0);\n\n float v00 = mix(v000, v100, t.x);\n float v01 = mix(v001, v101, t.x);\n float v10 = mix(v010, v110, t.x);\n float v11 = mix(v011, v111, t.x);\n\n float v0 = mix(v00, v10, t.y);\n float v1 = mix(v01, v11, t.y);\n\n return mix(v0, v1, t.z);\n}\n\nvec3 fade(vec3 t) {\n return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);\n}\n\nfloat perlinNoise(vec3 position, float seed) {\n position += vec3(seed * 127.1, seed * 311.7, seed * 74.7);\n\n vec3 i = floor(position);\n vec3 f = fract(position);\n float h000 = hash31(i);\n float h001 = hash31(i + vec3(0, 0, 1));\n float h010 = hash31(i + vec3(0, 1, 0));\n float h011 = hash31(i + vec3(0, 1, 1));\n float h100 = hash31(i + vec3(1, 0, 0));\n float h101 = hash31(i + vec3(1, 0, 1));\n float h110 = hash31(i + vec3(1, 1, 0));\n float h111 = hash31(i + vec3(1, 1, 1));\n vec3 g000 = gradientPredefined(h000);\n vec3 g001 = gradientPredefined(h001);\n vec3 g010 = gradientPredefined(h010);\n vec3 g011 = gradientPredefined(h011);\n vec3 g100 = gradientPredefined(h100);\n vec3 g101 = gradientPredefined(h101);\n vec3 g110 = gradientPredefined(h110);\n vec3 g111 = gradientPredefined(h111);\n float v000 = dot(g000, f - vec3(0, 0, 0));\n float v001 = dot(g001, f - vec3(0, 0, 1));\n float v010 = dot(g010, f - vec3(0, 1, 0));\n float v011 = dot(g011, f - vec3(0, 1, 1));\n float v100 = dot(g100, f - vec3(1, 0, 0));\n float v101 = dot(g101, f - vec3(1, 0, 1));\n float v110 = dot(g110, f - vec3(1, 1, 0));\n float v111 = dot(g111, f - vec3(1, 1, 1));\n\n vec3 u = fade(f);\n return interpolateSafe(v000, v001, v010, v011, v100, v101, v110, v111, u);\n}\n\nfloat p_noise(vec3 position, int octaveCount, float persistence, float lacunarity) {\n float value = 0.0;\n float amplitude = 1.0;\n float frequency = 10.0;\n float maxValue = 0.0;\n octaveCount = clamp(octaveCount, 1, 8);\n\n for (int i = 0; i < octaveCount; i++) {\n float seed = float(i) * 0.7319;\n value += perlinNoise(position * frequency, seed) * amplitude;\n maxValue += amplitude;\n amplitude *= persistence;\n frequency *= lacunarity;\n }\n return value;\n}\n\nfloat get_max_amp(float persistence, float octaveCount) {\n persistence = clamp(persistence * 0.999, 0.0, 0.999);\n octaveCount = clamp(octaveCount, 1.0, 8.0);\n\n if (abs(persistence - 1.0) < 0.001) {\n return octaveCount;\n }\n\n return (1.0 - pow(persistence, octaveCount)) / (1.0 - persistence);\n}\n\nvoid main() {\n vec2 uv = v_patternUV;\n uv *= .5;\n \n float t = .2 * u_time;\n\n vec3 p = vec3(uv, t);\n\n float octCount = clamp(floor(u_octaveCount), 1.0, 8.0);\n float persistence = clamp(u_persistence, 0., 1.);\n float noise = p_noise(p, int(octCount), persistence, u_lacunarity);\n\n float max_amp = get_max_amp(persistence, octCount);\n float noise_normalized = clamp((noise + max_amp) / (2. * max_amp) + (u_proportion - .5), 0.0, 1.0);\n float sharpness = clamp(u_softness, 0., 1.);\n float smooth_w = 0.5 * max(fwidth(noise_normalized), 0.001);\n float res = smoothstep(\n .5 - .5 * sharpness - smooth_w,\n .5 + .5 * sharpness + smooth_w,\n noise_normalized\n );\n\n vec3 fgColor = u_colorFront.rgb * u_colorFront.a;\n float fgOpacity = u_colorFront.a;\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n float bgOpacity = u_colorBack.a;\n\n vec3 color = fgColor * res;\n float opacity = fgOpacity * res;\n\n color += bgColor * (1. - opacity);\n opacity += bgOpacity * (1. - opacity);\n\n ${colorBandingFix}\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface PerlinNoiseUniforms extends ShaderSizingUniforms {\n u_colorFront: [number, number, number, number];\n u_colorBack: [number, number, number, number];\n u_proportion: number;\n u_softness: number;\n u_octaveCount: number;\n u_persistence: number;\n u_lacunarity: number;\n}\n\nexport interface PerlinNoiseParams extends ShaderSizingParams, ShaderMotionParams {\n colorFront?: string;\n colorBack?: string;\n proportion?: number;\n softness?: number;\n octaveCount?: number;\n persistence?: number;\n lacunarity?: number;\n}\n"],
4
+ "sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, colorBandingFix } from '../shader-utils.js';\n\n/**\n * 3d Perlin noise; original algorithm: https://www.shadertoy.com/view/NlSGDz\n *\n * Uniforms:\n * - u_colorBack, u_colorFront (RGBA)\n * - u_proportion: (0..1) blend point between 2 colors (0.5 = equal distribution)\n * - u_softness: color transition sharpness (0 = hard edge, 1 = smooth fade)\n * - u_octaveCount: more octaves => more detailed pattern\n * - u_persistence: roughness, falloff between octaves\n * - u_lacunarity: frequency step, typically around 2, defines how compressed is the pattern\n *\n */\n\n// language=GLSL\nexport const perlinNoiseFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform vec4 u_colorFront;\nuniform vec4 u_colorBack;\nuniform float u_proportion;\nuniform float u_softness;\nuniform float u_octaveCount;\nuniform float u_persistence;\nuniform float u_lacunarity;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n\nfloat hash11(float p) {\n p = fract(p * 0.3183099) + 0.1;\n p *= p + 19.19;\n return fract(p * p);\n}\n\nfloat hash21(vec2 p) {\n p = fract(p * vec2(0.3183099, 0.3678794)) + 0.1;\n p += dot(p, p + 19.19);\n return fract(p.x * p.y);\n}\n\nfloat hash31(vec3 p) {\n p = fract(p * 0.3183099) + 0.1;\n p += dot(p, p.yzx + 19.19);\n return fract(p.x * (p.y + p.z));\n}\n\nvec3 hash33(vec3 p) {\n p = fract(p * 0.3183099) + 0.1;\n p += dot(p, p.yzx + 19.19);\n return fract(vec3(p.x * p.y, p.y * p.z, p.z * p.x));\n}\n\nvec3 gradientSafe(vec3 p) {\n vec3 h = hash33(p) * 2.0 - 1.;\n return normalize(h + 0.001);\n}\n\nvec3 gradientPredefined(float hash) {\n int idx = int(hash * 12.0) % 12;\n\n if (idx == 0) return vec3(1, 1, 0);\n if (idx == 1) return vec3(-1, 1, 0);\n if (idx == 2) return vec3(1, -1, 0);\n if (idx == 3) return vec3(-1, -1, 0);\n if (idx == 4) return vec3(1, 0, 1);\n if (idx == 5) return vec3(-1, 0, 1);\n if (idx == 6) return vec3(1, 0, -1);\n if (idx == 7) return vec3(-1, 0, -1);\n if (idx == 8) return vec3(0, 1, 1);\n if (idx == 9) return vec3(0, -1, 1);\n if (idx == 10) return vec3(0, 1, -1);\n return vec3(0, -1, -1);// idx == 11\n}\n\nfloat interpolateSafe(float v000, float v001, float v010, float v011,\nfloat v100, float v101, float v110, float v111, vec3 t) {\n t = clamp(t, 0.0, 1.0);\n\n float v00 = mix(v000, v100, t.x);\n float v01 = mix(v001, v101, t.x);\n float v10 = mix(v010, v110, t.x);\n float v11 = mix(v011, v111, t.x);\n\n float v0 = mix(v00, v10, t.y);\n float v1 = mix(v01, v11, t.y);\n\n return mix(v0, v1, t.z);\n}\n\nvec3 fade(vec3 t) {\n return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);\n}\n\nfloat perlinNoise(vec3 position, float seed) {\n position += vec3(seed * 127.1, seed * 311.7, seed * 74.7);\n\n vec3 i = floor(position);\n vec3 f = fract(position);\n float h000 = hash31(i);\n float h001 = hash31(i + vec3(0, 0, 1));\n float h010 = hash31(i + vec3(0, 1, 0));\n float h011 = hash31(i + vec3(0, 1, 1));\n float h100 = hash31(i + vec3(1, 0, 0));\n float h101 = hash31(i + vec3(1, 0, 1));\n float h110 = hash31(i + vec3(1, 1, 0));\n float h111 = hash31(i + vec3(1, 1, 1));\n vec3 g000 = gradientPredefined(h000);\n vec3 g001 = gradientPredefined(h001);\n vec3 g010 = gradientPredefined(h010);\n vec3 g011 = gradientPredefined(h011);\n vec3 g100 = gradientPredefined(h100);\n vec3 g101 = gradientPredefined(h101);\n vec3 g110 = gradientPredefined(h110);\n vec3 g111 = gradientPredefined(h111);\n float v000 = dot(g000, f - vec3(0, 0, 0));\n float v001 = dot(g001, f - vec3(0, 0, 1));\n float v010 = dot(g010, f - vec3(0, 1, 0));\n float v011 = dot(g011, f - vec3(0, 1, 1));\n float v100 = dot(g100, f - vec3(1, 0, 0));\n float v101 = dot(g101, f - vec3(1, 0, 1));\n float v110 = dot(g110, f - vec3(1, 1, 0));\n float v111 = dot(g111, f - vec3(1, 1, 1));\n\n vec3 u = fade(f);\n return interpolateSafe(v000, v001, v010, v011, v100, v101, v110, v111, u);\n}\n\nfloat p_noise(vec3 position, int octaveCount, float persistence, float lacunarity) {\n float value = 0.0;\n float amplitude = 1.0;\n float frequency = 10.0;\n float maxValue = 0.0;\n octaveCount = clamp(octaveCount, 1, 8);\n\n for (int i = 0; i < octaveCount; i++) {\n float seed = float(i) * 0.7319;\n value += perlinNoise(position * frequency, seed) * amplitude;\n maxValue += amplitude;\n amplitude *= persistence;\n frequency *= lacunarity;\n }\n return value;\n}\n\nfloat get_max_amp(float persistence, float octaveCount) {\n persistence = clamp(persistence * 0.999, 0.0, 0.999);\n octaveCount = clamp(octaveCount, 1.0, 8.0);\n\n if (abs(persistence - 1.0) < 0.001) {\n return octaveCount;\n }\n\n return (1.0 - pow(persistence, octaveCount)) / max(1e-4, (1.0 - persistence));\n}\n\nvoid main() {\n vec2 uv = v_patternUV;\n uv *= .5;\n \n float t = .2 * u_time;\n\n vec3 p = vec3(uv, t);\n\n float octCount = clamp(floor(u_octaveCount), 1.0, 8.0);\n float persistence = clamp(u_persistence, 0., 1.);\n float noise = p_noise(p, int(octCount), persistence, u_lacunarity);\n\n float max_amp = get_max_amp(persistence, octCount);\n float noise_normalized = clamp((noise + max_amp) / max(1e-4, (2. * max_amp)) + (u_proportion - .5), 0.0, 1.0);\n float sharpness = clamp(u_softness, 0., 1.);\n float smooth_w = 0.5 * max(fwidth(noise_normalized), 0.001);\n float res = smoothstep(\n .5 - .5 * sharpness - smooth_w,\n .5 + .5 * sharpness + smooth_w,\n noise_normalized\n );\n\n vec3 fgColor = u_colorFront.rgb * u_colorFront.a;\n float fgOpacity = u_colorFront.a;\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n float bgOpacity = u_colorBack.a;\n\n vec3 color = fgColor * res;\n float opacity = fgOpacity * res;\n\n color += bgColor * (1. - opacity);\n opacity += bgOpacity * (1. - opacity);\n\n ${colorBandingFix}\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface PerlinNoiseUniforms extends ShaderSizingUniforms {\n u_colorFront: [number, number, number, number];\n u_colorBack: [number, number, number, number];\n u_proportion: number;\n u_softness: number;\n u_octaveCount: number;\n u_persistence: number;\n u_lacunarity: number;\n}\n\nexport interface PerlinNoiseParams extends ShaderSizingParams, ShaderMotionParams {\n colorFront?: string;\n colorBack?: string;\n proportion?: number;\n softness?: number;\n octaveCount?: number;\n persistence?: number;\n lacunarity?: number;\n}\n"],
5
5
  "mappings": ";;;;;AACA,SAAS,kCAAsF;AAC/F,SAAS,WAAW,uBAAuB;AAgBpC,MAAM,4BAAoC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAa/C,0BAA0B;AAAA;AAAA;AAAA;AAAA,EAI1B,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkKP,eAAe;AAAA;AAAA;AAAA;AAAA;",
6
6
  "names": []
7
7
  }
@@ -42,8 +42,8 @@ out vec4 fragColor;
42
42
  ${declarePI}
43
43
 
44
44
  float beat(float time) {
45
- float first = pow(sin(time * TWO_PI), 10.);
46
- float second = pow(sin((time - 0.15) * TWO_PI), 10.);
45
+ float first = pow(abs(sin(time * TWO_PI)), 10.);
46
+ float second = pow(abs(sin((time - .15) * TWO_PI)), 10.);
47
47
 
48
48
  return clamp(first + 0.6 * second, 0.0, 1.0);
49
49
  }
@@ -55,7 +55,7 @@ float sst(float edge0, float edge1, float x) {
55
55
  float roundedBox(vec2 uv, vec2 halfSize, float distance, float cornerDistance, float thickness, float softness) {
56
56
  float borderDistance = abs(distance);
57
57
  float aa = 2. * fwidth(distance);
58
- float border = 1. - sst(mix(thickness, -thickness, softness), thickness + aa, borderDistance);
58
+ float border = 1. - sst(min(mix(thickness, -thickness, softness), thickness + aa), max(mix(thickness, -thickness, softness), thickness + aa), borderDistance);
59
59
  float cornerFadeCircles = 0.;
60
60
  cornerFadeCircles = mix(1., cornerFadeCircles, sst(0., 1., length((uv + halfSize) / thickness)));
61
61
  cornerFadeCircles = mix(1., cornerFadeCircles, sst(0., 1., length((uv - vec2(-halfSize.x, halfSize.y)) / thickness)));
@@ -109,8 +109,8 @@ void main() {
109
109
  float mY = mT + mB;
110
110
 
111
111
  if (u_aspectRatio > 0.) {
112
- float shapeRatio = canvasRatio * (1. - mX) / (1. - mY);
113
- float freeX = shapeRatio > 1. ? (1. - mX) * (1. - 1. / shapeRatio) : 0.;
112
+ float shapeRatio = canvasRatio * (1. - mX) / max(1. - mY, 1e-6);
113
+ float freeX = shapeRatio > 1. ? (1. - mX) * (1. - 1. / max(abs(shapeRatio), 1e-6)) : 0.;
114
114
  float freeY = shapeRatio < 1. ? (1. - mY) * (1. - shapeRatio) : 0.;
115
115
  mL += freeX * 0.5;
116
116
  mR += freeX * 0.5;
@@ -150,7 +150,7 @@ void main() {
150
150
  float smokeThickness = thickness + .2;
151
151
  smokeThickness = min(.4, max(smokeThickness, .1));
152
152
  smoke *= roundedBox(borderUV, halfSize, distance, cornerDistance, smokeThickness, 1.);
153
- smoke = 30. * pow(smoke, 2.);
153
+ smoke = 30. * smoke * smoke;
154
154
  smoke *= mix(0., .5, pow(u_smoke, 2.));
155
155
  smoke *= mix(1., pulse, u_pulse);
156
156
  smoke = clamp(smoke, 0., 1.);
@@ -196,7 +196,7 @@ void main() {
196
196
  float atg1 = fract(angle + time);
197
197
  float spotSize = .05 + .6 * pow(u_spotSize, 2.) + .05 * randVal.x;
198
198
  spotSize = mix(spotSize, .1, p);
199
- float sector = sst(.5 - spotSize, .5, atg1) * sst(.5 + spotSize, .5, atg1);
199
+ float sector = sst(.5 - spotSize, .5, atg1) * (1. - sst(.5, .5 + spotSize, atg1));
200
200
 
201
201
  sector *= mask;
202
202
  sector *= border;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/shaders/pulsing-border.ts"],
4
- "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, textureRandomizerGB, colorBandingFix } from '../shader-utils.js';\n\nexport const pulsingBorderMeta = {\n maxColorCount: 5,\n maxSpots: 4,\n} as const;\n\n/**\n * Color spots traveling around rectangular stroke (border)\n *\n * Uniforms:\n * - u_colorBack (RGBA)\n * - u_colors (vec4[]), u_colorsCount (float used as integer)\n * - u_roundness, u_thickness, u_softness: border parameters\n * - u_aspectRatio\n * - u_intensity: thickness of individual spots\n * - u_bloom: normal / additive color blending\n * - u_spotSize: angular size of spots\n * - u_spots (float used as int): number of spots rendered per color\n * - u_pulse: optional pulsing animation\n * - u_smoke, u_smokeSize: optional noisy shapes around the border\n *\n * - u_noiseTexture (sampler2D): pre-computed randomizer source\n *\n */\n\n// language=GLSL\nexport const pulsingBorderFragmentShader: string = `#version 300 es\nprecision lowp float;\n\nuniform float u_time;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colors[${pulsingBorderMeta.maxColorCount}];\nuniform float u_colorsCount;\nuniform float u_roundness;\nuniform float u_thickness;\nuniform float u_marginLeft;\nuniform float u_marginRight;\nuniform float u_marginTop;\nuniform float u_marginBottom;\nuniform float u_aspectRatio;\nuniform float u_softness;\nuniform float u_intensity;\nuniform float u_bloom;\nuniform float u_spotSize;\nuniform float u_spots;\nuniform float u_pulse;\nuniform float u_smoke;\nuniform float u_smokeSize;\n\nuniform sampler2D u_noiseTexture;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n\nfloat beat(float time) {\n float first = pow(sin(time * TWO_PI), 10.);\n float second = pow(sin((time - 0.15) * TWO_PI), 10.);\n\n return clamp(first + 0.6 * second, 0.0, 1.0);\n}\n\nfloat sst(float edge0, float edge1, float x) {\n return smoothstep(edge0, edge1, x);\n}\n\nfloat roundedBox(vec2 uv, vec2 halfSize, float distance, float cornerDistance, float thickness, float softness) {\n float borderDistance = abs(distance);\n float aa = 2. * fwidth(distance);\n float border = 1. - sst(mix(thickness, -thickness, softness), thickness + aa, borderDistance);\n float cornerFadeCircles = 0.;\n cornerFadeCircles = mix(1., cornerFadeCircles, sst(0., 1., length((uv + halfSize) / thickness)));\n cornerFadeCircles = mix(1., cornerFadeCircles, sst(0., 1., length((uv - vec2(-halfSize.x, halfSize.y)) / thickness)));\n cornerFadeCircles = mix(1., cornerFadeCircles, sst(0., 1., length((uv - vec2(halfSize.x, -halfSize.y)) / thickness)));\n cornerFadeCircles = mix(1., cornerFadeCircles, sst(0., 1., length((uv - halfSize) / thickness)));\n aa = fwidth(cornerDistance);\n float cornerFade = sst(0., mix(aa, thickness, softness), cornerDistance);\n cornerFade *= cornerFadeCircles;\n border += cornerFade;\n return border;\n}\n\n${textureRandomizerGB}\n\nfloat randomG(vec2 p) {\n vec2 uv = floor(p) / 100. + .5;\n return texture(u_noiseTexture, fract(uv)).g;\n}\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = randomG(i);\n float b = randomG(i + vec2(1.0, 0.0));\n float c = randomG(i + vec2(0.0, 1.0));\n float d = randomG(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\nvoid main() {\n const float firstFrameOffset = 109.;\n float t = 1.2 * (u_time + firstFrameOffset);\n\n vec2 borderUV = v_responsiveUV;\n float pulse = u_pulse * beat(.18 * u_time);\n\n float canvasRatio = v_responsiveBoxGivenSize.x / v_responsiveBoxGivenSize.y;\n vec2 halfSize = vec2(.5);\n borderUV.x *= max(canvasRatio, 1.);\n borderUV.y /= min(canvasRatio, 1.);\n halfSize.x *= max(canvasRatio, 1.);\n halfSize.y /= min(canvasRatio, 1.);\n\n float mL = u_marginLeft;\n float mR = u_marginRight;\n float mT = u_marginTop;\n float mB = u_marginBottom;\n float mX = mL + mR;\n float mY = mT + mB;\n\n if (u_aspectRatio > 0.) {\n float shapeRatio = canvasRatio * (1. - mX) / (1. - mY);\n float freeX = shapeRatio > 1. ? (1. - mX) * (1. - 1. / shapeRatio) : 0.;\n float freeY = shapeRatio < 1. ? (1. - mY) * (1. - shapeRatio) : 0.;\n mL += freeX * 0.5;\n mR += freeX * 0.5;\n mT += freeY * 0.5;\n mB += freeY * 0.5;\n mX = mL + mR;\n mY = mT + mB;\n }\n\n float thickness = .5 * u_thickness * min(halfSize.x, halfSize.y);\n\n halfSize.x *= (1. - mX);\n halfSize.y *= (1. - mY);\n\n vec2 centerShift = vec2(\n (mL - mR) * max(canvasRatio, 1.) * 0.5,\n (mB - mT) / min(canvasRatio, 1.) * 0.5\n );\n\n borderUV -= centerShift;\n halfSize -= mix(thickness, 0., u_softness);\n\n float radius = mix(0., min(halfSize.x, halfSize.y), u_roundness);\n vec2 d = abs(borderUV) - halfSize + radius;\n float outsideDistance = length(max(d, .0001)) - radius;\n float insideDistance = min(max(d.x, d.y), .0001);\n float cornerDistance = abs(min(max(d.x, d.y) - .45 * radius, .0));\n float distance = outsideDistance + insideDistance;\n\n float borderThickness = mix(thickness, 3. * thickness, u_softness);\n float border = roundedBox(borderUV, halfSize, distance, cornerDistance, borderThickness, u_softness);\n border = pow(border, 1. + u_softness);\n\n vec2 smokeUV = .3 * u_smokeSize * v_patternUV;\n float smoke = clamp(3. * valueNoise(2.7 * smokeUV + .5 * t), 0., 1.);\n smoke -= valueNoise(3.4 * smokeUV - .5 * t);\n float smokeThickness = thickness + .2;\n smokeThickness = min(.4, max(smokeThickness, .1));\n smoke *= roundedBox(borderUV, halfSize, distance, cornerDistance, smokeThickness, 1.);\n smoke = 30. * pow(smoke, 2.);\n smoke *= mix(0., .5, pow(u_smoke, 2.));\n smoke *= mix(1., pulse, u_pulse);\n smoke = clamp(smoke, 0., 1.);\n border += smoke;\n\n border = clamp(border, 0., 1.);\n\n vec3 blendColor = vec3(0.);\n float blendAlpha = 0.;\n vec3 addColor = vec3(0.);\n float addAlpha = 0.;\n\n float bloom = 4. * u_bloom;\n float intensity = 1. + (1. + 4. * u_softness) * u_intensity;\n\n float angle = atan(borderUV.y, borderUV.x) / TWO_PI;\n\n for (int colorIdx = 0; colorIdx < ${pulsingBorderMeta.maxColorCount}; colorIdx++) {\n if (colorIdx >= int(u_colorsCount)) break;\n float colorIdxF = float(colorIdx);\n\n vec3 c = u_colors[colorIdx].rgb * u_colors[colorIdx].a;\n float a = u_colors[colorIdx].a;\n\n for (int spotIdx = 0; spotIdx < ${pulsingBorderMeta.maxSpots}; spotIdx++) {\n if (spotIdx >= int(u_spots)) break;\n float spotIdxF = float(spotIdx);\n\n vec2 randVal = randomGB(vec2(spotIdxF * 10. + 2., 40. + colorIdxF));\n\n float time = (.1 + .15 * abs(sin(spotIdxF * (2. + colorIdxF)) * cos(spotIdxF * (2. + 2.5 * colorIdxF)))) * t + randVal.x * 3.;\n time *= mix(1., -1., step(.5, randVal.y));\n\n float mask = .5 + .5 * mix(\n sin(t + spotIdxF * (5. - 1.5 * colorIdxF)),\n cos(t + spotIdxF * (3. + 1.3 * colorIdxF)),\n step(mod(colorIdxF, 2.), .5)\n );\n\n float p = clamp(2. * u_pulse - randVal.x, 0., 1.);\n mask = mix(mask, pulse, p);\n\n float atg1 = fract(angle + time);\n float spotSize = .05 + .6 * pow(u_spotSize, 2.) + .05 * randVal.x;\n spotSize = mix(spotSize, .1, p);\n float sector = sst(.5 - spotSize, .5, atg1) * sst(.5 + spotSize, .5, atg1);\n\n sector *= mask;\n sector *= border;\n sector *= intensity;\n sector = clamp(sector, 0., 1.);\n\n vec3 srcColor = c * sector;\n float srcAlpha = a * sector;\n\n blendColor += ((1. - blendAlpha) * srcColor);\n blendAlpha = blendAlpha + (1. - blendAlpha) * srcAlpha;\n addColor += srcColor;\n addAlpha += srcAlpha;\n }\n }\n\n vec3 accumColor = mix(blendColor, addColor, bloom);\n float accumAlpha = mix(blendAlpha, addAlpha, bloom);\n accumAlpha = clamp(accumAlpha, 0., 1.);\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n vec3 color = accumColor + (1. - accumAlpha) * bgColor;\n float opacity = accumAlpha + (1. - accumAlpha) * u_colorBack.a;\n\n ${colorBandingFix}\n\n fragColor = vec4(color, opacity);\n}`;\n\nexport interface PulsingBorderUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n u_roundness: number;\n u_thickness: number;\n u_marginLeft: number;\n u_marginRight: number;\n u_marginTop: number;\n u_marginBottom: number;\n u_aspectRatio: (typeof PulsingBorderAspectRatios)[PulsingBorderAspectRatio];\n u_softness: number;\n u_intensity: number;\n u_bloom: number;\n u_spots: number;\n u_spotSize: number;\n u_pulse: number;\n u_smoke: number;\n u_smokeSize: number;\n u_noiseTexture?: HTMLImageElement;\n}\n\nexport interface PulsingBorderParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colors?: string[];\n roundness?: number;\n thickness?: number;\n margin?: number;\n marginLeft?: number;\n marginRight?: number;\n marginTop?: number;\n marginBottom?: number;\n aspectRatio?: PulsingBorderAspectRatio;\n softness?: number;\n intensity?: number;\n bloom?: number;\n spots?: number;\n spotSize?: number;\n pulse?: number;\n smoke?: number;\n smokeSize?: number;\n}\n\nexport const PulsingBorderAspectRatios = {\n auto: 0,\n square: 1,\n} as const;\n\nexport type PulsingBorderAspectRatio = keyof typeof PulsingBorderAspectRatios;\n"],
4
+ "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, textureRandomizerGB, colorBandingFix } from '../shader-utils.js';\n\nexport const pulsingBorderMeta = {\n maxColorCount: 5,\n maxSpots: 4,\n} as const;\n\n/**\n * Color spots traveling around rectangular stroke (border)\n *\n * Uniforms:\n * - u_colorBack (RGBA)\n * - u_colors (vec4[]), u_colorsCount (float used as integer)\n * - u_roundness, u_thickness, u_softness: border parameters\n * - u_aspectRatio\n * - u_intensity: thickness of individual spots\n * - u_bloom: normal / additive color blending\n * - u_spotSize: angular size of spots\n * - u_spots (float used as int): number of spots rendered per color\n * - u_pulse: optional pulsing animation\n * - u_smoke, u_smokeSize: optional noisy shapes around the border\n *\n * - u_noiseTexture (sampler2D): pre-computed randomizer source\n *\n */\n\n// language=GLSL\nexport const pulsingBorderFragmentShader: string = `#version 300 es\nprecision lowp float;\n\nuniform float u_time;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colors[${pulsingBorderMeta.maxColorCount}];\nuniform float u_colorsCount;\nuniform float u_roundness;\nuniform float u_thickness;\nuniform float u_marginLeft;\nuniform float u_marginRight;\nuniform float u_marginTop;\nuniform float u_marginBottom;\nuniform float u_aspectRatio;\nuniform float u_softness;\nuniform float u_intensity;\nuniform float u_bloom;\nuniform float u_spotSize;\nuniform float u_spots;\nuniform float u_pulse;\nuniform float u_smoke;\nuniform float u_smokeSize;\n\nuniform sampler2D u_noiseTexture;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n\nfloat beat(float time) {\n float first = pow(abs(sin(time * TWO_PI)), 10.);\n float second = pow(abs(sin((time - .15) * TWO_PI)), 10.);\n\n return clamp(first + 0.6 * second, 0.0, 1.0);\n}\n\nfloat sst(float edge0, float edge1, float x) {\n return smoothstep(edge0, edge1, x);\n}\n\nfloat roundedBox(vec2 uv, vec2 halfSize, float distance, float cornerDistance, float thickness, float softness) {\n float borderDistance = abs(distance);\n float aa = 2. * fwidth(distance);\n float border = 1. - sst(min(mix(thickness, -thickness, softness), thickness + aa), max(mix(thickness, -thickness, softness), thickness + aa), borderDistance);\n float cornerFadeCircles = 0.;\n cornerFadeCircles = mix(1., cornerFadeCircles, sst(0., 1., length((uv + halfSize) / thickness)));\n cornerFadeCircles = mix(1., cornerFadeCircles, sst(0., 1., length((uv - vec2(-halfSize.x, halfSize.y)) / thickness)));\n cornerFadeCircles = mix(1., cornerFadeCircles, sst(0., 1., length((uv - vec2(halfSize.x, -halfSize.y)) / thickness)));\n cornerFadeCircles = mix(1., cornerFadeCircles, sst(0., 1., length((uv - halfSize) / thickness)));\n aa = fwidth(cornerDistance);\n float cornerFade = sst(0., mix(aa, thickness, softness), cornerDistance);\n cornerFade *= cornerFadeCircles;\n border += cornerFade;\n return border;\n}\n\n${textureRandomizerGB}\n\nfloat randomG(vec2 p) {\n vec2 uv = floor(p) / 100. + .5;\n return texture(u_noiseTexture, fract(uv)).g;\n}\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = randomG(i);\n float b = randomG(i + vec2(1.0, 0.0));\n float c = randomG(i + vec2(0.0, 1.0));\n float d = randomG(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\nvoid main() {\n const float firstFrameOffset = 109.;\n float t = 1.2 * (u_time + firstFrameOffset);\n\n vec2 borderUV = v_responsiveUV;\n float pulse = u_pulse * beat(.18 * u_time);\n\n float canvasRatio = v_responsiveBoxGivenSize.x / v_responsiveBoxGivenSize.y;\n vec2 halfSize = vec2(.5);\n borderUV.x *= max(canvasRatio, 1.);\n borderUV.y /= min(canvasRatio, 1.);\n halfSize.x *= max(canvasRatio, 1.);\n halfSize.y /= min(canvasRatio, 1.);\n\n float mL = u_marginLeft;\n float mR = u_marginRight;\n float mT = u_marginTop;\n float mB = u_marginBottom;\n float mX = mL + mR;\n float mY = mT + mB;\n\n if (u_aspectRatio > 0.) {\n float shapeRatio = canvasRatio * (1. - mX) / max(1. - mY, 1e-6);\n float freeX = shapeRatio > 1. ? (1. - mX) * (1. - 1. / max(abs(shapeRatio), 1e-6)) : 0.;\n float freeY = shapeRatio < 1. ? (1. - mY) * (1. - shapeRatio) : 0.;\n mL += freeX * 0.5;\n mR += freeX * 0.5;\n mT += freeY * 0.5;\n mB += freeY * 0.5;\n mX = mL + mR;\n mY = mT + mB;\n }\n\n float thickness = .5 * u_thickness * min(halfSize.x, halfSize.y);\n\n halfSize.x *= (1. - mX);\n halfSize.y *= (1. - mY);\n\n vec2 centerShift = vec2(\n (mL - mR) * max(canvasRatio, 1.) * 0.5,\n (mB - mT) / min(canvasRatio, 1.) * 0.5\n );\n\n borderUV -= centerShift;\n halfSize -= mix(thickness, 0., u_softness);\n\n float radius = mix(0., min(halfSize.x, halfSize.y), u_roundness);\n vec2 d = abs(borderUV) - halfSize + radius;\n float outsideDistance = length(max(d, .0001)) - radius;\n float insideDistance = min(max(d.x, d.y), .0001);\n float cornerDistance = abs(min(max(d.x, d.y) - .45 * radius, .0));\n float distance = outsideDistance + insideDistance;\n\n float borderThickness = mix(thickness, 3. * thickness, u_softness);\n float border = roundedBox(borderUV, halfSize, distance, cornerDistance, borderThickness, u_softness);\n border = pow(border, 1. + u_softness);\n\n vec2 smokeUV = .3 * u_smokeSize * v_patternUV;\n float smoke = clamp(3. * valueNoise(2.7 * smokeUV + .5 * t), 0., 1.);\n smoke -= valueNoise(3.4 * smokeUV - .5 * t);\n float smokeThickness = thickness + .2;\n smokeThickness = min(.4, max(smokeThickness, .1));\n smoke *= roundedBox(borderUV, halfSize, distance, cornerDistance, smokeThickness, 1.);\n smoke = 30. * smoke * smoke;\n smoke *= mix(0., .5, pow(u_smoke, 2.));\n smoke *= mix(1., pulse, u_pulse);\n smoke = clamp(smoke, 0., 1.);\n border += smoke;\n\n border = clamp(border, 0., 1.);\n\n vec3 blendColor = vec3(0.);\n float blendAlpha = 0.;\n vec3 addColor = vec3(0.);\n float addAlpha = 0.;\n\n float bloom = 4. * u_bloom;\n float intensity = 1. + (1. + 4. * u_softness) * u_intensity;\n\n float angle = atan(borderUV.y, borderUV.x) / TWO_PI;\n\n for (int colorIdx = 0; colorIdx < ${pulsingBorderMeta.maxColorCount}; colorIdx++) {\n if (colorIdx >= int(u_colorsCount)) break;\n float colorIdxF = float(colorIdx);\n\n vec3 c = u_colors[colorIdx].rgb * u_colors[colorIdx].a;\n float a = u_colors[colorIdx].a;\n\n for (int spotIdx = 0; spotIdx < ${pulsingBorderMeta.maxSpots}; spotIdx++) {\n if (spotIdx >= int(u_spots)) break;\n float spotIdxF = float(spotIdx);\n\n vec2 randVal = randomGB(vec2(spotIdxF * 10. + 2., 40. + colorIdxF));\n\n float time = (.1 + .15 * abs(sin(spotIdxF * (2. + colorIdxF)) * cos(spotIdxF * (2. + 2.5 * colorIdxF)))) * t + randVal.x * 3.;\n time *= mix(1., -1., step(.5, randVal.y));\n\n float mask = .5 + .5 * mix(\n sin(t + spotIdxF * (5. - 1.5 * colorIdxF)),\n cos(t + spotIdxF * (3. + 1.3 * colorIdxF)),\n step(mod(colorIdxF, 2.), .5)\n );\n\n float p = clamp(2. * u_pulse - randVal.x, 0., 1.);\n mask = mix(mask, pulse, p);\n\n float atg1 = fract(angle + time);\n float spotSize = .05 + .6 * pow(u_spotSize, 2.) + .05 * randVal.x;\n spotSize = mix(spotSize, .1, p);\n float sector = sst(.5 - spotSize, .5, atg1) * (1. - sst(.5, .5 + spotSize, atg1));\n\n sector *= mask;\n sector *= border;\n sector *= intensity;\n sector = clamp(sector, 0., 1.);\n\n vec3 srcColor = c * sector;\n float srcAlpha = a * sector;\n\n blendColor += ((1. - blendAlpha) * srcColor);\n blendAlpha = blendAlpha + (1. - blendAlpha) * srcAlpha;\n addColor += srcColor;\n addAlpha += srcAlpha;\n }\n }\n\n vec3 accumColor = mix(blendColor, addColor, bloom);\n float accumAlpha = mix(blendAlpha, addAlpha, bloom);\n accumAlpha = clamp(accumAlpha, 0., 1.);\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n vec3 color = accumColor + (1. - accumAlpha) * bgColor;\n float opacity = accumAlpha + (1. - accumAlpha) * u_colorBack.a;\n\n ${colorBandingFix}\n\n fragColor = vec4(color, opacity);\n}`;\n\nexport interface PulsingBorderUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n u_roundness: number;\n u_thickness: number;\n u_marginLeft: number;\n u_marginRight: number;\n u_marginTop: number;\n u_marginBottom: number;\n u_aspectRatio: (typeof PulsingBorderAspectRatios)[PulsingBorderAspectRatio];\n u_softness: number;\n u_intensity: number;\n u_bloom: number;\n u_spots: number;\n u_spotSize: number;\n u_pulse: number;\n u_smoke: number;\n u_smokeSize: number;\n u_noiseTexture?: HTMLImageElement;\n}\n\nexport interface PulsingBorderParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colors?: string[];\n roundness?: number;\n thickness?: number;\n margin?: number;\n marginLeft?: number;\n marginRight?: number;\n marginTop?: number;\n marginBottom?: number;\n aspectRatio?: PulsingBorderAspectRatio;\n softness?: number;\n intensity?: number;\n bloom?: number;\n spots?: number;\n spotSize?: number;\n pulse?: number;\n smoke?: number;\n smokeSize?: number;\n}\n\nexport const PulsingBorderAspectRatios = {\n auto: 0,\n square: 1,\n} as const;\n\nexport type PulsingBorderAspectRatio = keyof typeof PulsingBorderAspectRatios;\n"],
5
5
  "mappings": ";;;;;AAEA,SAAS,kCAAsF;AAC/F,SAAS,WAAW,qBAAqB,uBAAuB;AAEzD,MAAM,oBAAoB;AAAA,EAC/B,eAAe;AAAA,EACf,UAAU;AACZ;AAsBO,MAAM,8BAAsC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAM3B,kBAAkB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBrD,0BAA0B;AAAA;AAAA;AAAA;AAAA,EAI1B,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA6BT,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sCAoGiB,kBAAkB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sCAO/B,kBAAkB,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA8C5D,eAAe;AAAA;AAAA;AAAA;AAgDZ,MAAM,4BAA4B;AAAA,EACvC,MAAM;AAAA,EACN,QAAQ;AACV;",
6
6
  "names": []
7
7
  }
@@ -57,7 +57,8 @@ float fbm(in vec2 n) {
57
57
 
58
58
  float getNoise(vec2 uv, vec2 pUv, float t) {
59
59
  float noiseLeft = fbm(pUv + .03 * t);
60
- pUv.x = mod(pUv.x, u_noiseScale * TWO_PI);
60
+ float period = max(abs(u_noiseScale * TWO_PI), 1e-6);
61
+ pUv.x = fract(pUv.x / period) * period;
61
62
  float noiseRight = fbm(pUv + .03 * t);
62
63
  return mix(noiseRight, noiseLeft, smoothstep(-.25, .25, uv.x));
63
64
  }
@@ -79,17 +80,18 @@ void main() {
79
80
  float t = u_time;
80
81
 
81
82
  float cycleDuration = 3.;
82
- float localTime1 = mod(.1 * t + cycleDuration, 2. * cycleDuration);
83
- float localTime2 = mod(.1 * t, 2. * cycleDuration);
83
+ float period2 = 2.0 * cycleDuration;
84
+ float localTime1 = fract((0.1 * t + cycleDuration) / period2) * period2;
85
+ float localTime2 = fract((0.1 * t) / period2) * period2;
84
86
  float timeBlend = .5 + .5 * sin(.1 * t * PI / cycleDuration - .5 * PI);
85
87
 
86
88
  float atg = atan(shape_uv.y, shape_uv.x) + .001;
87
89
  float l = length(shape_uv);
88
- vec2 polar_uv1 = vec2(atg, localTime1 - (.5 * l) + 1. / pow(l, .5));
90
+ vec2 polar_uv1 = vec2(atg, localTime1 - (.5 * l) + 1. / pow(max(1e-4, l), .5));
89
91
  polar_uv1 *= u_noiseScale;
90
92
  float noise1 = getNoise(shape_uv, polar_uv1, t);
91
93
 
92
- vec2 polar_uv2 = vec2(atg, localTime2 - (.5 * l) + 1. / pow(l, .5));
94
+ vec2 polar_uv2 = vec2(atg, localTime2 - (.5 * l) + 1. / pow(max(1e-4, l), .5));
93
95
  polar_uv2 *= u_noiseScale;
94
96
  float noise2 = getNoise(shape_uv, polar_uv2, t);
95
97
 
@@ -99,7 +101,7 @@ void main() {
99
101
 
100
102
  float ringShape = getRingShape(shape_uv);
101
103
 
102
- float mixer = pow(ringShape, 2.) * (u_colorsCount - 1.);
104
+ float mixer = ringShape * ringShape * (u_colorsCount - 1.);
103
105
  vec4 gradient = u_colors[int(u_colorsCount) - 1];
104
106
  gradient.rgb *= gradient.a;
105
107
  for (int i = ${smokeRingMeta.maxColorCount} - 2; i >= 0; i--) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/shaders/smoke-ring.ts"],
4
- "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, textureRandomizerR, colorBandingFix } from '../shader-utils.js';\n\nexport const smokeRingMeta = {\n maxColorCount: 10,\n maxNoiseIterations: 8,\n} as const;\n\n/**\n * Radial gradient with layered FBM displacement, masked with ring shape\n *\n * Uniforms:\n * - u_colorBack (RGBA)\n * - u_colors (vec4[]), u_colorsCount (float used as integer)\n * - u_thickness, u_radius, u_innerShape: ring mask settings\n * - u_noiseIterations, u_noiseScale: how detailed is the noise (number of fbm layers & noise frequency)\n *\n * - u_noiseTexture (sampler2D): pre-computed randomizer source\n */\n\n// language=GLSL\nexport const smokeRingFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform sampler2D u_noiseTexture;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colors[${smokeRingMeta.maxColorCount}];\nuniform float u_colorsCount;\n\nuniform float u_thickness;\nuniform float u_radius;\nuniform float u_innerShape;\nuniform float u_noiseScale;\nuniform float u_noiseIterations;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n${textureRandomizerR}\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = randomR(i);\n float b = randomR(i + vec2(1.0, 0.0));\n float c = randomR(i + vec2(0.0, 1.0));\n float d = randomR(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\nfloat fbm(in vec2 n) {\n float total = 0.0, amplitude = .4;\n for (int i = 0; i < ${smokeRingMeta.maxNoiseIterations}; i++) {\n if (i >= int(u_noiseIterations)) break;\n total += valueNoise(n) * amplitude;\n n *= 1.99;\n amplitude *= 0.65;\n }\n return total;\n}\n\nfloat getNoise(vec2 uv, vec2 pUv, float t) {\n float noiseLeft = fbm(pUv + .03 * t);\n pUv.x = mod(pUv.x, u_noiseScale * TWO_PI);\n float noiseRight = fbm(pUv + .03 * t);\n return mix(noiseRight, noiseLeft, smoothstep(-.25, .25, uv.x));\n}\n\nfloat getRingShape(vec2 uv) {\n float radius = u_radius;\n float thickness = u_thickness;\n\n float distance = length(uv);\n float ringValue = 1. - smoothstep(radius, radius + thickness, distance);\n ringValue *= smoothstep(radius - pow(u_innerShape, 3.) * thickness, radius, distance);\n\n return ringValue;\n}\n\nvoid main() {\n vec2 shape_uv = v_objectUV;\n\n float t = u_time;\n\n float cycleDuration = 3.;\n float localTime1 = mod(.1 * t + cycleDuration, 2. * cycleDuration);\n float localTime2 = mod(.1 * t, 2. * cycleDuration);\n float timeBlend = .5 + .5 * sin(.1 * t * PI / cycleDuration - .5 * PI);\n\n float atg = atan(shape_uv.y, shape_uv.x) + .001;\n float l = length(shape_uv);\n vec2 polar_uv1 = vec2(atg, localTime1 - (.5 * l) + 1. / pow(l, .5));\n polar_uv1 *= u_noiseScale;\n float noise1 = getNoise(shape_uv, polar_uv1, t);\n\n vec2 polar_uv2 = vec2(atg, localTime2 - (.5 * l) + 1. / pow(l, .5));\n polar_uv2 *= u_noiseScale;\n float noise2 = getNoise(shape_uv, polar_uv2, t);\n\n float noise = mix(noise1, noise2, timeBlend);\n\n shape_uv *= (.8 + 1.2 * noise);\n\n float ringShape = getRingShape(shape_uv);\n\n float mixer = pow(ringShape, 2.) * (u_colorsCount - 1.);\n vec4 gradient = u_colors[int(u_colorsCount) - 1];\n gradient.rgb *= gradient.a;\n for (int i = ${smokeRingMeta.maxColorCount} - 2; i >= 0; i--) {\n float localT = clamp(mixer - float(int(u_colorsCount) - 1 - i - 1), 0., 1.);\n vec4 c = u_colors[i];\n c.rgb *= c.a;\n gradient = mix(gradient, c, localT);\n }\n\n vec3 color = gradient.rgb * ringShape;\n float opacity = gradient.a * ringShape;\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n color = color + bgColor * (1. - opacity);\n opacity = opacity + u_colorBack.a * (1. - opacity);\n\n ${colorBandingFix}\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface SmokeRingUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n u_noiseScale: number;\n u_thickness: number;\n u_radius: number;\n u_innerShape: number;\n u_noiseIterations: number;\n u_noiseTexture?: HTMLImageElement;\n}\n\nexport interface SmokeRingParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colors?: string[];\n noiseScale?: number;\n thickness?: number;\n radius?: number;\n innerShape?: number;\n noiseIterations?: number;\n}\n"],
5
- "mappings": ";;;;;AAEA,SAAS,kCAAsF;AAC/F,SAAS,WAAW,oBAAoB,uBAAuB;AAExD,MAAM,gBAAgB;AAAA,EAC3B,eAAe;AAAA,EACf,oBAAoB;AACtB;AAeO,MAAM,0BAAkC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAQvB,cAAc,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASjD,0BAA0B;AAAA;AAAA;AAAA;AAAA,EAI1B,SAAS;AAAA,EACT,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAeI,cAAc,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAwDvC,cAAc,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAcxC,eAAe;AAAA;AAAA;AAAA;AAAA;",
4
+ "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, textureRandomizerR, colorBandingFix } from '../shader-utils.js';\n\nexport const smokeRingMeta = {\n maxColorCount: 10,\n maxNoiseIterations: 8,\n} as const;\n\n/**\n * Radial gradient with layered FBM displacement, masked with ring shape\n *\n * Uniforms:\n * - u_colorBack (RGBA)\n * - u_colors (vec4[]), u_colorsCount (float used as integer)\n * - u_thickness, u_radius, u_innerShape: ring mask settings\n * - u_noiseIterations, u_noiseScale: how detailed is the noise (number of fbm layers & noise frequency)\n *\n * - u_noiseTexture (sampler2D): pre-computed randomizer source\n */\n\n// language=GLSL\nexport const smokeRingFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform sampler2D u_noiseTexture;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colors[${smokeRingMeta.maxColorCount}];\nuniform float u_colorsCount;\n\nuniform float u_thickness;\nuniform float u_radius;\nuniform float u_innerShape;\nuniform float u_noiseScale;\nuniform float u_noiseIterations;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n${textureRandomizerR}\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = randomR(i);\n float b = randomR(i + vec2(1.0, 0.0));\n float c = randomR(i + vec2(0.0, 1.0));\n float d = randomR(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\nfloat fbm(in vec2 n) {\n float total = 0.0, amplitude = .4;\n for (int i = 0; i < ${smokeRingMeta.maxNoiseIterations}; i++) {\n if (i >= int(u_noiseIterations)) break;\n total += valueNoise(n) * amplitude;\n n *= 1.99;\n amplitude *= 0.65;\n }\n return total;\n}\n\nfloat getNoise(vec2 uv, vec2 pUv, float t) {\n float noiseLeft = fbm(pUv + .03 * t);\n float period = max(abs(u_noiseScale * TWO_PI), 1e-6);\n pUv.x = fract(pUv.x / period) * period;\n float noiseRight = fbm(pUv + .03 * t);\n return mix(noiseRight, noiseLeft, smoothstep(-.25, .25, uv.x));\n}\n\nfloat getRingShape(vec2 uv) {\n float radius = u_radius;\n float thickness = u_thickness;\n\n float distance = length(uv);\n float ringValue = 1. - smoothstep(radius, radius + thickness, distance);\n ringValue *= smoothstep(radius - pow(u_innerShape, 3.) * thickness, radius, distance);\n\n return ringValue;\n}\n\nvoid main() {\n vec2 shape_uv = v_objectUV;\n\n float t = u_time;\n\n float cycleDuration = 3.;\n float period2 = 2.0 * cycleDuration;\n float localTime1 = fract((0.1 * t + cycleDuration) / period2) * period2;\n float localTime2 = fract((0.1 * t) / period2) * period2;\n float timeBlend = .5 + .5 * sin(.1 * t * PI / cycleDuration - .5 * PI);\n\n float atg = atan(shape_uv.y, shape_uv.x) + .001;\n float l = length(shape_uv);\n vec2 polar_uv1 = vec2(atg, localTime1 - (.5 * l) + 1. / pow(max(1e-4, l), .5));\n polar_uv1 *= u_noiseScale;\n float noise1 = getNoise(shape_uv, polar_uv1, t);\n\n vec2 polar_uv2 = vec2(atg, localTime2 - (.5 * l) + 1. / pow(max(1e-4, l), .5));\n polar_uv2 *= u_noiseScale;\n float noise2 = getNoise(shape_uv, polar_uv2, t);\n\n float noise = mix(noise1, noise2, timeBlend);\n\n shape_uv *= (.8 + 1.2 * noise);\n\n float ringShape = getRingShape(shape_uv);\n\n float mixer = ringShape * ringShape * (u_colorsCount - 1.);\n vec4 gradient = u_colors[int(u_colorsCount) - 1];\n gradient.rgb *= gradient.a;\n for (int i = ${smokeRingMeta.maxColorCount} - 2; i >= 0; i--) {\n float localT = clamp(mixer - float(int(u_colorsCount) - 1 - i - 1), 0., 1.);\n vec4 c = u_colors[i];\n c.rgb *= c.a;\n gradient = mix(gradient, c, localT);\n }\n\n vec3 color = gradient.rgb * ringShape;\n float opacity = gradient.a * ringShape;\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n color = color + bgColor * (1. - opacity);\n opacity = opacity + u_colorBack.a * (1. - opacity);\n\n ${colorBandingFix}\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface SmokeRingUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n u_noiseScale: number;\n u_thickness: number;\n u_radius: number;\n u_innerShape: number;\n u_noiseIterations: number;\n u_noiseTexture?: HTMLImageElement;\n}\n\nexport interface SmokeRingParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colors?: string[];\n noiseScale?: number;\n thickness?: number;\n radius?: number;\n innerShape?: number;\n noiseIterations?: number;\n}\n"],
5
+ "mappings": ";;;;;AAEA,SAAS,kCAAsF;AAC/F,SAAS,WAAW,oBAAoB,uBAAuB;AAExD,MAAM,gBAAgB;AAAA,EAC3B,eAAe;AAAA,EACf,oBAAoB;AACtB;AAeO,MAAM,0BAAkC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAQvB,cAAc,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASjD,0BAA0B;AAAA;AAAA;AAAA;AAAA,EAI1B,SAAS;AAAA,EACT,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAeI,cAAc,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBA0DvC,cAAc,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAcxC,eAAe;AAAA;AAAA;AAAA;AAAA;",
6
6
  "names": []
7
7
  }
@@ -34,7 +34,7 @@ void main() {
34
34
  float t = u_time;
35
35
  float l = length(uv);
36
36
  float density = clamp(u_density, 0., 1.);
37
- l = pow(l, density);
37
+ l = pow(max(l, 1e-6), density);
38
38
  float angle = atan(uv.y, uv.x) - t;
39
39
  float angleNormalised = angle / TWO_PI;
40
40
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/shaders/spiral.ts"],
4
- "sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { simplexNoise, declarePI, colorBandingFix } from '../shader-utils.js';\n\n/**\n * 2-color spiral shape\n *\n * Uniforms:\n * - u_colorBack, u_colorFront (RGBA)\n * - u_density: spacing falloff to simulate radial perspective (0 = no perspective)\n * - u_strokeWidth: thickness of stroke\n * - u_strokeTaper: stroke loosing width further from center (0 for full visibility)\n * - u_distortion: per-arch shift\n * - u_strokeCap: extra width at the center (no effect on u_strokeWidth = 0.5)\n * - u_noiseFrequency, u_noise: simplex noise distortion over the shape\n * - u_softness: color transition sharpness (0 = hard edge, 1 = smooth fade)\n *\n */\n\n// language=GLSL\nexport const spiralFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colorFront;\nuniform float u_density;\nuniform float u_distortion;\nuniform float u_strokeWidth;\nuniform float u_strokeCap;\nuniform float u_strokeTaper;\nuniform float u_noise;\nuniform float u_noiseFrequency;\nuniform float u_softness;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n${simplexNoise}\n\nvoid main() {\n vec2 uv = 2. * v_patternUV;\n \n float t = u_time;\n float l = length(uv);\n float density = clamp(u_density, 0., 1.);\n l = pow(l, density);\n float angle = atan(uv.y, uv.x) - t;\n float angleNormalised = angle / TWO_PI;\n\n angleNormalised += .125 * u_noise * snoise(16. * pow(u_noiseFrequency, 3.) * uv);\n\n float offset = l + angleNormalised;\n offset -= u_distortion * (sin(4. * l - .5 * t) * cos(PI + l + .5 * t));\n float stripe = fract(offset);\n \n float shape = 2. * abs(stripe - .5);\n float width = 1. - clamp(u_strokeWidth, .005 * u_strokeTaper, 1.);\n\n\n float wCap = mix(width, (1. - stripe) * (1. - step(.5, stripe)), (1. - clamp(l, 0., 1.)));\n width = mix(width, wCap, u_strokeCap);\n width *= (1. - clamp(u_strokeTaper, 0., 1.) * l);\n\n float fw = fwidth(offset);\n float fwMult = 4. - 3. * (smoothstep(.05, .4, 2. * u_strokeWidth) * smoothstep(.05, .4, 2. * (1. - u_strokeWidth)));\n float pixelSize = mix(fwMult * fw, fwidth(shape), clamp(fw, 0., 1.));\n pixelSize = mix(pixelSize, .002, u_strokeCap * (1. - clamp(l, 0., 1.)));\n\n float res = smoothstep(width - pixelSize - u_softness, width + pixelSize + u_softness, shape);\n\n vec3 fgColor = u_colorFront.rgb * u_colorFront.a;\n float fgOpacity = u_colorFront.a;\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n float bgOpacity = u_colorBack.a;\n\n vec3 color = fgColor * res;\n float opacity = fgOpacity * res;\n\n color += bgColor * (1. - opacity);\n opacity += bgOpacity * (1. - opacity);\n\n ${colorBandingFix}\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface SpiralUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colorFront: [number, number, number, number];\n u_density: number;\n u_distortion: number;\n u_strokeWidth: number;\n u_strokeTaper: number;\n u_strokeCap: number;\n u_noise: number;\n u_noiseFrequency: number;\n u_softness: number;\n}\n\nexport interface SpiralParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colorFront?: string;\n density?: number;\n distortion?: number;\n strokeWidth?: number;\n strokeTaper?: number;\n strokeCap?: number;\n noise?: number;\n noiseFrequency?: number;\n softness?: number;\n}\n"],
4
+ "sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { simplexNoise, declarePI, colorBandingFix } from '../shader-utils.js';\n\n/**\n * 2-color spiral shape\n *\n * Uniforms:\n * - u_colorBack, u_colorFront (RGBA)\n * - u_density: spacing falloff to simulate radial perspective (0 = no perspective)\n * - u_strokeWidth: thickness of stroke\n * - u_strokeTaper: stroke loosing width further from center (0 for full visibility)\n * - u_distortion: per-arch shift\n * - u_strokeCap: extra width at the center (no effect on u_strokeWidth = 0.5)\n * - u_noiseFrequency, u_noise: simplex noise distortion over the shape\n * - u_softness: color transition sharpness (0 = hard edge, 1 = smooth fade)\n *\n */\n\n// language=GLSL\nexport const spiralFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colorFront;\nuniform float u_density;\nuniform float u_distortion;\nuniform float u_strokeWidth;\nuniform float u_strokeCap;\nuniform float u_strokeTaper;\nuniform float u_noise;\nuniform float u_noiseFrequency;\nuniform float u_softness;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n${simplexNoise}\n\nvoid main() {\n vec2 uv = 2. * v_patternUV;\n \n float t = u_time;\n float l = length(uv);\n float density = clamp(u_density, 0., 1.);\n l = pow(max(l, 1e-6), density);\n float angle = atan(uv.y, uv.x) - t;\n float angleNormalised = angle / TWO_PI;\n\n angleNormalised += .125 * u_noise * snoise(16. * pow(u_noiseFrequency, 3.) * uv);\n\n float offset = l + angleNormalised;\n offset -= u_distortion * (sin(4. * l - .5 * t) * cos(PI + l + .5 * t));\n float stripe = fract(offset);\n \n float shape = 2. * abs(stripe - .5);\n float width = 1. - clamp(u_strokeWidth, .005 * u_strokeTaper, 1.);\n\n\n float wCap = mix(width, (1. - stripe) * (1. - step(.5, stripe)), (1. - clamp(l, 0., 1.)));\n width = mix(width, wCap, u_strokeCap);\n width *= (1. - clamp(u_strokeTaper, 0., 1.) * l);\n\n float fw = fwidth(offset);\n float fwMult = 4. - 3. * (smoothstep(.05, .4, 2. * u_strokeWidth) * smoothstep(.05, .4, 2. * (1. - u_strokeWidth)));\n float pixelSize = mix(fwMult * fw, fwidth(shape), clamp(fw, 0., 1.));\n pixelSize = mix(pixelSize, .002, u_strokeCap * (1. - clamp(l, 0., 1.)));\n\n float res = smoothstep(width - pixelSize - u_softness, width + pixelSize + u_softness, shape);\n\n vec3 fgColor = u_colorFront.rgb * u_colorFront.a;\n float fgOpacity = u_colorFront.a;\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n float bgOpacity = u_colorBack.a;\n\n vec3 color = fgColor * res;\n float opacity = fgOpacity * res;\n\n color += bgColor * (1. - opacity);\n opacity += bgOpacity * (1. - opacity);\n\n ${colorBandingFix}\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface SpiralUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colorFront: [number, number, number, number];\n u_density: number;\n u_distortion: number;\n u_strokeWidth: number;\n u_strokeTaper: number;\n u_strokeCap: number;\n u_noise: number;\n u_noiseFrequency: number;\n u_softness: number;\n}\n\nexport interface SpiralParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colorFront?: string;\n density?: number;\n distortion?: number;\n strokeWidth?: number;\n strokeTaper?: number;\n strokeCap?: number;\n noise?: number;\n noiseFrequency?: number;\n softness?: number;\n}\n"],
5
5
  "mappings": ";;;;;AACA,SAAS,kCAAsF;AAC/F,SAAS,cAAc,WAAW,uBAAuB;AAkBlD,MAAM,uBAA+B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgB1C,0BAA0B;AAAA;AAAA;AAAA;AAAA,EAI1B,SAAS;AAAA,EACT,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA4CV,eAAe;AAAA;AAAA;AAAA;AAAA;",
6
6
  "names": []
7
7
  }
@@ -120,8 +120,8 @@ void main() {
120
120
  totalWeight += w;
121
121
  }
122
122
 
123
- color /= totalWeight;
124
- opacity /= totalWeight;
123
+ color /= max(0.001, totalWeight);
124
+ opacity /= max(0.001, totalWeight);
125
125
 
126
126
  float rr = noise(rotate(grainUV, 1.), vec2(3.));
127
127
  float gg = noise(rotate(grainUV, 2.) + 10., vec2(-1.));
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/shaders/static-mesh-gradient.ts"],
4
- "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport {\n sizingDebugVariablesDeclaration,\n sizingVariablesDeclaration,\n sizingUniformsDeclaration,\n type ShaderSizingParams,\n type ShaderSizingUniforms,\n} from '../shader-sizing.js';\nimport { declarePI, rotation2, proceduralHash21 } from '../shader-utils.js';\n\nexport const staticMeshGradientMeta = {\n maxColorCount: 10,\n} as const;\n\n/**\n * A composition of N color spots (one per color)\n *\n * Uniforms:\n * - u_colorBack (RGBA)\n * - u_colors (vec4[]), u_colorsCount (float used as integer)\n * - u_waveX, u_waveY - power of sine wave distortion along X and Y axes\n * - u_waveXShift, u_waveYShift - each wave phase offset\n * - u_mixing (0 .. 1, float) - 0 for stepped gradient, 0.5 for smooth transitions, 1 for pronounced color points\n * - u_grainMixer - shape distortion\n * - u_grainOverlay - post-processing blending\n */\n\n// language=GLSL\nexport const staticMeshGradientFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform vec4 u_colors[${staticMeshGradientMeta.maxColorCount}];\nuniform float u_colorsCount;\n\nuniform float u_positions;\nuniform float u_waveX;\nuniform float u_waveXShift;\nuniform float u_waveY;\nuniform float u_waveYShift;\nuniform float u_mixing;\nuniform float u_grainMixer;\nuniform float u_grainOverlay;\n\n${sizingVariablesDeclaration}\n${sizingDebugVariablesDeclaration}\n${sizingUniformsDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n${rotation2}\n${proceduralHash21}\n\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = hash21(i);\n float b = hash21(i + vec2(1.0, 0.0));\n float c = hash21(i + vec2(0.0, 1.0));\n float d = hash21(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\n\nfloat noise(vec2 n, vec2 seedOffset) {\n return valueNoise(n + seedOffset);\n}\n\nvec2 getPosition(int i, float t) {\n float a = float(i) * .37;\n float b = .6 + mod(float(i), 3.) * .3;\n float c = .8 + mod(float(i + 1), 4.) * 0.25;\n\n float x = sin(t * b + a);\n float y = cos(t * c + a * 1.5);\n\n return .5 + .5 * vec2(x, y);\n}\n\nvoid main() {\n vec2 uv = v_objectUV;\n uv += .5;\n\n vec2 grainUV = v_objectUV;\n // apply inverse transform to grain_uv so it respects the originXY\n float grainUVRot = u_rotation * 3.14159265358979323846 / 180.;\n mat2 graphicRotation = mat2(cos(grainUVRot), sin(grainUVRot), -sin(grainUVRot), cos(grainUVRot));\n vec2 graphicOffset = vec2(-u_offsetX, u_offsetY);\n grainUV = transpose(graphicRotation) * grainUV;\n grainUV *= u_scale;\n grainUV *= .7;\n grainUV -= graphicOffset;\n grainUV *= v_objectBoxSize;\n\n float grain = noise(grainUV, vec2(0.));\n float mixerGrain = .4 * u_grainMixer * (grain - .5);\n\n float radius = smoothstep(0., 1., length(uv - .5));\n float center = 1. - radius;\n for (float i = 1.; i <= 2.; i++) {\n uv.x += u_waveX * center / i * cos(TWO_PI * u_waveXShift + i * 2. * smoothstep(.0, 1., uv.y));\n uv.y += u_waveY * center / i * cos(TWO_PI * u_waveYShift + i * 2. * smoothstep(.0, 1., uv.x));\n }\n \n vec3 color = vec3(0.);\n float opacity = 0.;\n float totalWeight = 0.;\n float positionSeed = 25. + .33 * u_positions;\n\n for (int i = 0; i < ${staticMeshGradientMeta.maxColorCount}; i++) {\n if (i >= int(u_colorsCount)) break;\n\n vec2 pos = getPosition(i, positionSeed) + mixerGrain;\n float dist = length(uv - pos);\n dist = length(uv - pos);\n\n vec3 colorFraction = u_colors[i].rgb * u_colors[i].a;\n float opacityFraction = u_colors[i].a;\n\n float power = 4.;\n if (u_mixing > .5) {\n power = mix(power, .75, 2. * (u_mixing - .5));\n }\n dist = pow(dist, power);\n\n float w = 1. / (dist + 1e-3);\n if (u_mixing < .5) {\n w = pow(w, mix(mix(.01, 5., clamp(w, 0., 1.)), 1., 2. * u_mixing));\n }\n color += colorFraction * w;\n opacity += opacityFraction * w;\n totalWeight += w;\n }\n\n color /= totalWeight;\n opacity /= totalWeight;\n\n float rr = noise(rotate(grainUV, 1.), vec2(3.));\n float gg = noise(rotate(grainUV, 2.) + 10., vec2(-1.));\n float bb = noise(grainUV - 2., vec2(5.));\n vec3 grainColor = vec3(rr, gg, bb);\n color = mix(color, grainColor, .01 + .3 * u_grainOverlay);\n \n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface StaticMeshGradientUniforms extends ShaderSizingUniforms {\n u_colors: vec4[];\n u_colorsCount: number;\n u_positions: number;\n u_waveX: number;\n u_waveXShift: number;\n u_waveY: number;\n u_waveYShift: number;\n u_mixing: number;\n u_grainMixer: number;\n u_grainOverlay: number;\n}\n\nexport interface StaticMeshGradientParams extends ShaderSizingParams, ShaderMotionParams {\n colors?: string[];\n positions?: number;\n waveX?: number;\n waveXShift?: number;\n waveY?: number;\n waveYShift?: number;\n mixing?: number;\n grainMixer?: number;\n grainOverlay?: number;\n}\n"],
4
+ "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport {\n sizingDebugVariablesDeclaration,\n sizingVariablesDeclaration,\n sizingUniformsDeclaration,\n type ShaderSizingParams,\n type ShaderSizingUniforms,\n} from '../shader-sizing.js';\nimport { declarePI, rotation2, proceduralHash21 } from '../shader-utils.js';\n\nexport const staticMeshGradientMeta = {\n maxColorCount: 10,\n} as const;\n\n/**\n * A composition of N color spots (one per color)\n *\n * Uniforms:\n * - u_colorBack (RGBA)\n * - u_colors (vec4[]), u_colorsCount (float used as integer)\n * - u_waveX, u_waveY - power of sine wave distortion along X and Y axes\n * - u_waveXShift, u_waveYShift - each wave phase offset\n * - u_mixing (0 .. 1, float) - 0 for stepped gradient, 0.5 for smooth transitions, 1 for pronounced color points\n * - u_grainMixer - shape distortion\n * - u_grainOverlay - post-processing blending\n */\n\n// language=GLSL\nexport const staticMeshGradientFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform vec4 u_colors[${staticMeshGradientMeta.maxColorCount}];\nuniform float u_colorsCount;\n\nuniform float u_positions;\nuniform float u_waveX;\nuniform float u_waveXShift;\nuniform float u_waveY;\nuniform float u_waveYShift;\nuniform float u_mixing;\nuniform float u_grainMixer;\nuniform float u_grainOverlay;\n\n${sizingVariablesDeclaration}\n${sizingDebugVariablesDeclaration}\n${sizingUniformsDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n${rotation2}\n${proceduralHash21}\n\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = hash21(i);\n float b = hash21(i + vec2(1.0, 0.0));\n float c = hash21(i + vec2(0.0, 1.0));\n float d = hash21(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\n\nfloat noise(vec2 n, vec2 seedOffset) {\n return valueNoise(n + seedOffset);\n}\n\nvec2 getPosition(int i, float t) {\n float a = float(i) * .37;\n float b = .6 + mod(float(i), 3.) * .3;\n float c = .8 + mod(float(i + 1), 4.) * 0.25;\n\n float x = sin(t * b + a);\n float y = cos(t * c + a * 1.5);\n\n return .5 + .5 * vec2(x, y);\n}\n\nvoid main() {\n vec2 uv = v_objectUV;\n uv += .5;\n\n vec2 grainUV = v_objectUV;\n // apply inverse transform to grain_uv so it respects the originXY\n float grainUVRot = u_rotation * 3.14159265358979323846 / 180.;\n mat2 graphicRotation = mat2(cos(grainUVRot), sin(grainUVRot), -sin(grainUVRot), cos(grainUVRot));\n vec2 graphicOffset = vec2(-u_offsetX, u_offsetY);\n grainUV = transpose(graphicRotation) * grainUV;\n grainUV *= u_scale;\n grainUV *= .7;\n grainUV -= graphicOffset;\n grainUV *= v_objectBoxSize;\n\n float grain = noise(grainUV, vec2(0.));\n float mixerGrain = .4 * u_grainMixer * (grain - .5);\n\n float radius = smoothstep(0., 1., length(uv - .5));\n float center = 1. - radius;\n for (float i = 1.; i <= 2.; i++) {\n uv.x += u_waveX * center / i * cos(TWO_PI * u_waveXShift + i * 2. * smoothstep(.0, 1., uv.y));\n uv.y += u_waveY * center / i * cos(TWO_PI * u_waveYShift + i * 2. * smoothstep(.0, 1., uv.x));\n }\n \n vec3 color = vec3(0.);\n float opacity = 0.;\n float totalWeight = 0.;\n float positionSeed = 25. + .33 * u_positions;\n\n for (int i = 0; i < ${staticMeshGradientMeta.maxColorCount}; i++) {\n if (i >= int(u_colorsCount)) break;\n\n vec2 pos = getPosition(i, positionSeed) + mixerGrain;\n float dist = length(uv - pos);\n dist = length(uv - pos);\n\n vec3 colorFraction = u_colors[i].rgb * u_colors[i].a;\n float opacityFraction = u_colors[i].a;\n\n float power = 4.;\n if (u_mixing > .5) {\n power = mix(power, .75, 2. * (u_mixing - .5));\n }\n dist = pow(dist, power);\n\n float w = 1. / (dist + 1e-3);\n if (u_mixing < .5) {\n w = pow(w, mix(mix(.01, 5., clamp(w, 0., 1.)), 1., 2. * u_mixing));\n }\n color += colorFraction * w;\n opacity += opacityFraction * w;\n totalWeight += w;\n }\n\n color /= max(0.001, totalWeight);\n opacity /= max(0.001, totalWeight);\n\n float rr = noise(rotate(grainUV, 1.), vec2(3.));\n float gg = noise(rotate(grainUV, 2.) + 10., vec2(-1.));\n float bb = noise(grainUV - 2., vec2(5.));\n vec3 grainColor = vec3(rr, gg, bb);\n color = mix(color, grainColor, .01 + .3 * u_grainOverlay);\n \n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface StaticMeshGradientUniforms extends ShaderSizingUniforms {\n u_colors: vec4[];\n u_colorsCount: number;\n u_positions: number;\n u_waveX: number;\n u_waveXShift: number;\n u_waveY: number;\n u_waveYShift: number;\n u_mixing: number;\n u_grainMixer: number;\n u_grainOverlay: number;\n}\n\nexport interface StaticMeshGradientParams extends ShaderSizingParams, ShaderMotionParams {\n colors?: string[];\n positions?: number;\n waveX?: number;\n waveXShift?: number;\n waveY?: number;\n waveYShift?: number;\n mixing?: number;\n grainMixer?: number;\n grainOverlay?: number;\n}\n"],
5
5
  "mappings": ";;;;;AAEA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,WAAW,WAAW,wBAAwB;AAEhD,MAAM,yBAAyB;AAAA,EACpC,eAAe;AACjB;AAgBO,MAAM,mCAA2C;AAAA;AAAA;AAAA,wBAGhC,uBAAuB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAY1D,0BAA0B;AAAA,EAC1B,+BAA+B;AAAA,EAC/B,yBAAyB;AAAA;AAAA;AAAA;AAAA,EAIzB,SAAS;AAAA,EACT,SAAS;AAAA,EACT,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBA4DM,uBAAuB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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
  }
@@ -83,7 +83,7 @@ void main() {
83
83
  grainUV *= v_objectBoxSize;
84
84
 
85
85
  vec2 center = vec2(0.);
86
- float angleRad = radians(u_focalAngle - 90.);
86
+ float angleRad = -radians(u_focalAngle + 90.);
87
87
  vec2 focalPoint = vec2(cos(angleRad), sin(angleRad)) * u_focalDistance;
88
88
  float radius = u_radius;
89
89
 
@@ -93,11 +93,13 @@ void main() {
93
93
  float r = length(c_to_uv);
94
94
 
95
95
  float fragAngle = atan(c_to_uv.y, c_to_uv.x);
96
- float angleDiff = fragAngle - angleRad;
97
- angleDiff = mod(angleDiff + PI, TWO_PI) - PI;
98
-
99
- float halfAngle = acos(clamp(radius / u_focalDistance, 0.0, 1.0));
100
- float isInSector = 1.0 - smoothstep(.6 * PI, halfAngle, abs(angleDiff));
96
+ float angleDiff = fract((fragAngle - angleRad + PI) / TWO_PI) * TWO_PI - PI;
97
+
98
+ float halfAngle = acos(clamp(radius / max(u_focalDistance, 1e-4), 0.0, 1.0));
99
+ float e0 = 0.6 * PI, e1 = halfAngle;
100
+ float lo = min(e0, e1), hi = max(e0, e1);
101
+ float s = smoothstep(lo, hi, abs(angleDiff));
102
+ float isInSector = (e1 >= e0) ? (1.0 - s) : s;
101
103
 
102
104
  float a = dot(f_to_uv, f_to_uv);
103
105
  float b = -2.0 * dot(f_to_uv, f_to_c);
@@ -108,17 +110,18 @@ void main() {
108
110
 
109
111
  if (discriminant >= 0.0) {
110
112
  float sqrtD = sqrt(discriminant);
111
- float t0 = (-b - sqrtD) / (2.0 * a);
112
- float t1 = (-b + sqrtD) / (2.0 * a);
113
+ float div = max(1e-4, 2.0 * a);
114
+ float t0 = (-b - sqrtD) / div;
115
+ float t1 = (-b + sqrtD) / div;
113
116
  t = max(t0, t1);
114
117
  if (t < 0.0) t = 0.0;
115
118
  }
116
119
 
117
120
  float dist = length(f_to_uv);
118
- float normalized = dist / (length(f_to_uv * t));
121
+ float normalized = dist / max(1e-4, length(f_to_uv * t));
119
122
  float shape = clamp(normalized, 0.0, 1.0);
120
123
 
121
- float falloffMapped = mix(.2 + .8 * max(0., u_falloff + 1.), mix(1., 15., pow(u_falloff, 2.)), step(.0, u_falloff));
124
+ float falloffMapped = mix(.2 + .8 * max(0., u_falloff + 1.), mix(1., 15., u_falloff * u_falloff), step(.0, u_falloff));
122
125
 
123
126
  float falloffExp = mix(falloffMapped, 1., shape);
124
127
  shape = pow(shape, falloffExp);
@@ -126,14 +129,14 @@ void main() {
126
129
 
127
130
 
128
131
  float outerMask = .002;
129
- float outer = smoothstep(radius + outerMask, radius - outerMask, r);
132
+ float outer = 1.0 - smoothstep(radius - outerMask, radius + outerMask, r);
130
133
  outer = mix(outer, 1., isInSector);
131
134
 
132
135
  shape = mix(0., shape, outer);
133
- shape *= smoothstep(radius, radius - .01, r);
136
+ shape *= 1. - smoothstep(radius - .01, radius, r);
134
137
 
135
138
  float angle = atan(f_to_uv.y, f_to_uv.x);
136
- shape -= pow(u_distortion, 2.) * shape * pow(sin(PI * clamp(length(f_to_uv) - .2 + u_distortionShift, 0., 1.)), 4.) * (sin(u_distortionFreq * angle) + cos(floor(.65 * u_distortionFreq) * angle));
139
+ shape -= pow(u_distortion, 2.) * shape * pow(abs(sin(PI * clamp(length(f_to_uv) - 0.2 + u_distortionShift, 0.0, 1.0))), 4.0) * (sin(u_distortionFreq * angle) + cos(floor(0.65 * u_distortionFreq) * angle));
137
140
 
138
141
  float grain = noise(grainUV, vec2(0.));
139
142
  float mixerGrain = .4 * u_grainMixer * (grain - .5);
@@ -150,7 +153,7 @@ void main() {
150
153
  float m = 0.;
151
154
  float mixing = u_mixing * 3.;
152
155
  if (mixing > 2.) {
153
- float tt = pow(mLinear, 2.);
156
+ float tt = mLinear * mLinear;
154
157
  m = mix(mLinear, tt, .5 * clamp((mixing - 2.), 0., 1.));
155
158
  } else if (mixing > 1.) {
156
159
  m = mix(smoothstep(0., 1., mLinear), mLinear, clamp((mixing - 1.), 0., 1.));
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/shaders/static-radial-gradient.ts"],
4
- "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport {\n sizingDebugVariablesDeclaration,\n sizingVariablesDeclaration,\n sizingUniformsDeclaration,\n type ShaderSizingParams,\n type ShaderSizingUniforms,\n} from '../shader-sizing.js';\nimport { declarePI, rotation2, proceduralHash21 } from '../shader-utils.js';\n\nexport const staticRadialGradientMeta = {\n maxColorCount: 10,\n} as const;\n\n/**\n * N-colors radial gradient\n *\n * Uniforms:\n * - u_colorBack (RGBA)\n * - u_colors (vec4[]), u_colorsCount (float used as integer)\n * - u_radius - circle radius\n * - u_focalDistance, u_focalAngle (float) - gradient center offset to the circle center\n * - u_falloff (-1 .. 1, float) - color points distribution (0 for linear gradient)\n * - u_mixing (0 .. 1, float) - 0 for stepped gradient, 0.5 for smooth transitions, 1 for pronounced color points\n * - u_distortion, u_distortionShift, u_distortionFreq - radial distortion (effective with u_distortion > 0)\n * - u_grainMixer - shape distortion\n * - u_grainOverlay - post-processing blending\n */\n\n// language=GLSL\nexport const staticRadialGradientFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colors[${staticRadialGradientMeta.maxColorCount}];\nuniform float u_colorsCount;\n\nuniform float u_radius;\nuniform float u_focalDistance;\nuniform float u_focalAngle;\nuniform float u_falloff;\nuniform float u_mixing;\nuniform float u_distortion;\nuniform float u_distortionShift;\nuniform float u_distortionFreq;\nuniform float u_grainMixer;\nuniform float u_grainOverlay;\n\n${sizingVariablesDeclaration}\n${sizingDebugVariablesDeclaration}\n${sizingUniformsDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n${rotation2}\n${proceduralHash21}\n\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = hash21(i);\n float b = hash21(i + vec2(1.0, 0.0));\n float c = hash21(i + vec2(0.0, 1.0));\n float d = hash21(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\n\nfloat noise(vec2 n, vec2 seedOffset) {\n return valueNoise(n + seedOffset);\n}\n\nvec2 getPosition(int i, float t) {\n float a = float(i) * .37;\n float b = .6 + mod(float(i), 3.) * .3;\n float c = .8 + mod(float(i + 1), 4.) * 0.25;\n\n float x = sin(t * b + a);\n float y = cos(t * c + a * 1.5);\n\n return .5 + .5 * vec2(x, y);\n}\n\nvoid main() {\n vec2 uv = 2. * v_objectUV;\n\n vec2 grainUV = v_objectUV;\n // apply inverse transform to grain_uv so it respects the originXY\n float grainUVRot = u_rotation * 3.14159265358979323846 / 180.;\n mat2 graphicRotation = mat2(cos(grainUVRot), sin(grainUVRot), -sin(grainUVRot), cos(grainUVRot));\n vec2 graphicOffset = vec2(-u_offsetX, u_offsetY);\n grainUV = transpose(graphicRotation) * grainUV;\n grainUV *= u_scale;\n grainUV *= .7;\n grainUV -= graphicOffset;\n grainUV *= v_objectBoxSize;\n\n vec2 center = vec2(0.);\n float angleRad = radians(u_focalAngle - 90.);\n vec2 focalPoint = vec2(cos(angleRad), sin(angleRad)) * u_focalDistance;\n float radius = u_radius;\n \n vec2 c_to_uv = uv - center;\n vec2 f_to_uv = uv - focalPoint;\n vec2 f_to_c = center - focalPoint;\n float r = length(c_to_uv);\n \n float fragAngle = atan(c_to_uv.y, c_to_uv.x);\n float angleDiff = fragAngle - angleRad;\n angleDiff = mod(angleDiff + PI, TWO_PI) - PI;\n \n float halfAngle = acos(clamp(radius / u_focalDistance, 0.0, 1.0));\n float isInSector = 1.0 - smoothstep(.6 * PI, halfAngle, abs(angleDiff));\n \n float a = dot(f_to_uv, f_to_uv);\n float b = -2.0 * dot(f_to_uv, f_to_c);\n float c = dot(f_to_c, f_to_c) - radius * radius;\n\n float discriminant = b * b - 4.0 * a * c;\n float t = 1.0;\n\n if (discriminant >= 0.0) {\n float sqrtD = sqrt(discriminant);\n float t0 = (-b - sqrtD) / (2.0 * a);\n float t1 = (-b + sqrtD) / (2.0 * a);\n t = max(t0, t1);\n if (t < 0.0) t = 0.0;\n }\n\n float dist = length(f_to_uv);\n float normalized = dist / (length(f_to_uv * t));\n float shape = clamp(normalized, 0.0, 1.0);\n\n float falloffMapped = mix(.2 + .8 * max(0., u_falloff + 1.), mix(1., 15., pow(u_falloff, 2.)), step(.0, u_falloff));\n \n float falloffExp = mix(falloffMapped, 1., shape);\n shape = pow(shape, falloffExp);\n shape = 1. - clamp(shape, 0., 1.);\n\n\n float outerMask = .002;\n float outer = smoothstep(radius + outerMask, radius - outerMask, r);\n outer = mix(outer, 1., isInSector);\n \n shape = mix(0., shape, outer);\n shape *= smoothstep(radius, radius - .01, r);\n\n float angle = atan(f_to_uv.y, f_to_uv.x);\n shape -= pow(u_distortion, 2.) * shape * pow(sin(PI * clamp(length(f_to_uv) - .2 + u_distortionShift, 0., 1.)), 4.) * (sin(u_distortionFreq * angle) + cos(floor(.65 * u_distortionFreq) * angle));\n\n float grain = noise(grainUV, vec2(0.));\n float mixerGrain = .4 * u_grainMixer * (grain - .5);\n\n float mixer = shape * u_colorsCount + mixerGrain;\n vec4 gradient = u_colors[0];\n gradient.rgb *= gradient.a;\n \n float outerShape = 0.;\n for (int i = 1; i < ${staticRadialGradientMeta.maxColorCount + 1}; i++) {\n if (i > int(u_colorsCount)) break;\n float mLinear = clamp(mixer - float(i - 1), 0.0, 1.0);\n \n float m = 0.;\n float mixing = u_mixing * 3.;\n if (mixing > 2.) {\n float tt = pow(mLinear, 2.);\n m = mix(mLinear, tt, .5 * clamp((mixing - 2.), 0., 1.));\n } else if (mixing > 1.) {\n m = mix(smoothstep(0., 1., mLinear), mLinear, clamp((mixing - 1.), 0., 1.));\n } else {\n float aa = fwidth(mLinear);\n m = smoothstep(.5 - .5 * mixing - aa, .5 + .5 * mixing + aa, mLinear);\n }\n \n if (i == 1) {\n outerShape = m;\n }\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 float rr = noise(rotate(grainUV, 1.), vec2(3.));\n float gg = noise(rotate(grainUV, 2.) + 10., vec2(-1.));\n float bb = noise(grainUV - 2., vec2(5.));\n vec3 grainColor = vec3(rr, gg, bb);\n color = mix(color, grainColor, .01 + .3 * u_grainOverlay);\n opacity += u_grainOverlay * grain;\n \n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface StaticRadialGradientUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n u_radius: number;\n u_focalDistance: number;\n u_focalAngle: number;\n u_falloff: number;\n u_mixing: number;\n u_distortion: number;\n u_distortionShift: number;\n u_distortionFreq: number;\n u_grainMixer: number;\n u_grainOverlay: number;\n}\n\nexport interface StaticRadialGradientParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colors?: string[];\n radius?: number;\n focalDistance?: number;\n focalAngle?: number;\n falloff?: number;\n mixing?: number;\n distortion?: number;\n distortionShift?: number;\n distortionFreq?: number;\n grainMixer?: number;\n grainOverlay?: number;\n}\n"],
5
- "mappings": ";;;;;AAEA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,WAAW,WAAW,wBAAwB;AAEhD,MAAM,2BAA2B;AAAA,EACtC,eAAe;AACjB;AAkBO,MAAM,qCAA6C;AAAA;AAAA;AAAA;AAAA,wBAIlC,yBAAyB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAc5D,0BAA0B;AAAA,EAC1B,+BAA+B;AAAA,EAC/B,yBAAyB;AAAA;AAAA;AAAA;AAAA,EAIzB,SAAS;AAAA,EACT,SAAS;AAAA,EACT,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAyGM,yBAAyB,gBAAgB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
4
+ "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport {\n sizingDebugVariablesDeclaration,\n sizingVariablesDeclaration,\n sizingUniformsDeclaration,\n type ShaderSizingParams,\n type ShaderSizingUniforms,\n} from '../shader-sizing.js';\nimport { declarePI, rotation2, proceduralHash21 } from '../shader-utils.js';\n\nexport const staticRadialGradientMeta = {\n maxColorCount: 10,\n} as const;\n\n/**\n * N-colors radial gradient\n *\n * Uniforms:\n * - u_colorBack (RGBA)\n * - u_colors (vec4[]), u_colorsCount (float used as integer)\n * - u_radius - circle radius\n * - u_focalDistance, u_focalAngle (float) - gradient center offset to the circle center\n * - u_falloff (-1 .. 1, float) - color points distribution (0 for linear gradient)\n * - u_mixing (0 .. 1, float) - 0 for stepped gradient, 0.5 for smooth transitions, 1 for pronounced color points\n * - u_distortion, u_distortionShift, u_distortionFreq - radial distortion (effective with u_distortion > 0)\n * - u_grainMixer - shape distortion\n * - u_grainOverlay - post-processing blending\n */\n\n// language=GLSL\nexport const staticRadialGradientFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colors[${staticRadialGradientMeta.maxColorCount}];\nuniform float u_colorsCount;\n\nuniform float u_radius;\nuniform float u_focalDistance;\nuniform float u_focalAngle;\nuniform float u_falloff;\nuniform float u_mixing;\nuniform float u_distortion;\nuniform float u_distortionShift;\nuniform float u_distortionFreq;\nuniform float u_grainMixer;\nuniform float u_grainOverlay;\n\n${sizingVariablesDeclaration}\n${sizingDebugVariablesDeclaration}\n${sizingUniformsDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n${rotation2}\n${proceduralHash21}\n\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = hash21(i);\n float b = hash21(i + vec2(1.0, 0.0));\n float c = hash21(i + vec2(0.0, 1.0));\n float d = hash21(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\n\nfloat noise(vec2 n, vec2 seedOffset) {\n return valueNoise(n + seedOffset);\n}\n\nvec2 getPosition(int i, float t) {\n float a = float(i) * .37;\n float b = .6 + mod(float(i), 3.) * .3;\n float c = .8 + mod(float(i + 1), 4.) * 0.25;\n\n float x = sin(t * b + a);\n float y = cos(t * c + a * 1.5);\n\n return .5 + .5 * vec2(x, y);\n}\n\nvoid main() {\n vec2 uv = 2. * v_objectUV;\n\n vec2 grainUV = v_objectUV;\n // apply inverse transform to grain_uv so it respects the originXY\n float grainUVRot = u_rotation * 3.14159265358979323846 / 180.;\n mat2 graphicRotation = mat2(cos(grainUVRot), sin(grainUVRot), -sin(grainUVRot), cos(grainUVRot));\n vec2 graphicOffset = vec2(-u_offsetX, u_offsetY);\n grainUV = transpose(graphicRotation) * grainUV;\n grainUV *= u_scale;\n grainUV *= .7;\n grainUV -= graphicOffset;\n grainUV *= v_objectBoxSize;\n\n vec2 center = vec2(0.);\n float angleRad = -radians(u_focalAngle + 90.);\n vec2 focalPoint = vec2(cos(angleRad), sin(angleRad)) * u_focalDistance;\n float radius = u_radius;\n \n vec2 c_to_uv = uv - center;\n vec2 f_to_uv = uv - focalPoint;\n vec2 f_to_c = center - focalPoint;\n float r = length(c_to_uv);\n \n float fragAngle = atan(c_to_uv.y, c_to_uv.x);\n float angleDiff = fract((fragAngle - angleRad + PI) / TWO_PI) * TWO_PI - PI;\n\n float halfAngle = acos(clamp(radius / max(u_focalDistance, 1e-4), 0.0, 1.0));\n float e0 = 0.6 * PI, e1 = halfAngle;\n float lo = min(e0, e1), hi = max(e0, e1);\n float s = smoothstep(lo, hi, abs(angleDiff));\n float isInSector = (e1 >= e0) ? (1.0 - s) : s;\n \n float a = dot(f_to_uv, f_to_uv);\n float b = -2.0 * dot(f_to_uv, f_to_c);\n float c = dot(f_to_c, f_to_c) - radius * radius;\n\n float discriminant = b * b - 4.0 * a * c;\n float t = 1.0;\n\n if (discriminant >= 0.0) {\n float sqrtD = sqrt(discriminant);\n float div = max(1e-4, 2.0 * a);\n float t0 = (-b - sqrtD) / div;\n float t1 = (-b + sqrtD) / div;\n t = max(t0, t1);\n if (t < 0.0) t = 0.0;\n }\n\n float dist = length(f_to_uv);\n float normalized = dist / max(1e-4, length(f_to_uv * t));\n float shape = clamp(normalized, 0.0, 1.0);\n\n float falloffMapped = mix(.2 + .8 * max(0., u_falloff + 1.), mix(1., 15., u_falloff * u_falloff), step(.0, u_falloff));\n \n float falloffExp = mix(falloffMapped, 1., shape);\n shape = pow(shape, falloffExp);\n shape = 1. - clamp(shape, 0., 1.);\n\n\n float outerMask = .002;\n float outer = 1.0 - smoothstep(radius - outerMask, radius + outerMask, r);\n outer = mix(outer, 1., isInSector);\n \n shape = mix(0., shape, outer);\n shape *= 1. - smoothstep(radius - .01, radius, r);\n\n float angle = atan(f_to_uv.y, f_to_uv.x);\n shape -= pow(u_distortion, 2.) * shape * pow(abs(sin(PI * clamp(length(f_to_uv) - 0.2 + u_distortionShift, 0.0, 1.0))), 4.0) * (sin(u_distortionFreq * angle) + cos(floor(0.65 * u_distortionFreq) * angle));\n\n float grain = noise(grainUV, vec2(0.));\n float mixerGrain = .4 * u_grainMixer * (grain - .5);\n\n float mixer = shape * u_colorsCount + mixerGrain;\n vec4 gradient = u_colors[0];\n gradient.rgb *= gradient.a;\n \n float outerShape = 0.;\n for (int i = 1; i < ${staticRadialGradientMeta.maxColorCount + 1}; i++) {\n if (i > int(u_colorsCount)) break;\n float mLinear = clamp(mixer - float(i - 1), 0.0, 1.0);\n \n float m = 0.;\n float mixing = u_mixing * 3.;\n if (mixing > 2.) {\n float tt = mLinear * mLinear;\n m = mix(mLinear, tt, .5 * clamp((mixing - 2.), 0., 1.));\n } else if (mixing > 1.) {\n m = mix(smoothstep(0., 1., mLinear), mLinear, clamp((mixing - 1.), 0., 1.));\n } else {\n float aa = fwidth(mLinear);\n m = smoothstep(.5 - .5 * mixing - aa, .5 + .5 * mixing + aa, mLinear);\n }\n \n if (i == 1) {\n outerShape = m;\n }\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 float rr = noise(rotate(grainUV, 1.), vec2(3.));\n float gg = noise(rotate(grainUV, 2.) + 10., vec2(-1.));\n float bb = noise(grainUV - 2., vec2(5.));\n vec3 grainColor = vec3(rr, gg, bb);\n color = mix(color, grainColor, .01 + .3 * u_grainOverlay);\n opacity += u_grainOverlay * grain;\n \n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface StaticRadialGradientUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n u_radius: number;\n u_focalDistance: number;\n u_focalAngle: number;\n u_falloff: number;\n u_mixing: number;\n u_distortion: number;\n u_distortionShift: number;\n u_distortionFreq: number;\n u_grainMixer: number;\n u_grainOverlay: number;\n}\n\nexport interface StaticRadialGradientParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colors?: string[];\n radius?: number;\n focalDistance?: number;\n focalAngle?: number;\n falloff?: number;\n mixing?: number;\n distortion?: number;\n distortionShift?: number;\n distortionFreq?: number;\n grainMixer?: number;\n grainOverlay?: number;\n}\n"],
5
+ "mappings": ";;;;;AAEA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,WAAW,WAAW,wBAAwB;AAEhD,MAAM,2BAA2B;AAAA,EACtC,eAAe;AACjB;AAkBO,MAAM,qCAA6C;AAAA;AAAA;AAAA;AAAA,wBAIlC,yBAAyB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAc5D,0BAA0B;AAAA,EAC1B,+BAA+B;AAAA,EAC/B,yBAAyB;AAAA;AAAA;AAAA;AAAA,EAIzB,SAAS;AAAA,EACT,SAAS;AAAA,EACT,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBA4GM,yBAAyB,gBAAgB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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
  }
@@ -36,7 +36,8 @@ void main() {
36
36
  vec2 shape_uv = v_objectUV;
37
37
 
38
38
  float l = length(shape_uv);
39
-
39
+ l = max(1e-4, l);
40
+
40
41
  float t = u_time;
41
42
 
42
43
  float angle = ceil(u_bandCount) * atan(shape_uv.y, shape_uv.x) + t;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/shaders/swirl.ts"],
4
- "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { simplexNoise, declarePI, rotation2, colorBandingFix } from '../shader-utils.js';\n\nexport const swirlMeta = {\n maxColorCount: 10,\n} as const;\n\n/**\n * Twisting radial bands\n *\n * Uniforms:\n * - u_colorBack (RGBA)\n * - u_colors (vec4[]), u_colorsCount (float used as integer)\n * - u_bandCount (float, used as int): number of sectors\n * - u_twist: sectors twist intensity (0 = linear)\n * - u_softness: color transition sharpness (0 = hard edge, 1 = smooth fade)\n * - u_noise, u_noiseFrequency: simplex noise distortion over the shape\n *\n */\n\n// language=GLSL\nexport const swirlFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colors[${swirlMeta.maxColorCount}];\nuniform float u_colorsCount;\nuniform float u_bandCount;\nuniform float u_twist;\nuniform float u_center;\nuniform float u_proportion;\nuniform float u_softness;\nuniform float u_noise;\nuniform float u_noiseFrequency;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n${simplexNoise}\n${rotation2}\n\nvoid main() {\n vec2 shape_uv = v_objectUV;\n\n float l = length(shape_uv);\n\n float t = u_time;\n\n float angle = ceil(u_bandCount) * atan(shape_uv.y, shape_uv.x) + t;\n float angle_norm = angle / TWO_PI;\n\n float twist = 3. * clamp(u_twist, 0., 1.);\n float offset = pow(l, -twist) + angle_norm;\n\n float shape = fract(offset);\n shape = 1. - abs(2. * shape - 1.);\n shape += u_noise * snoise(15. * pow(u_noiseFrequency, 2.) * shape_uv);\n\n float mid = smoothstep(.2, .2 + .8 * u_center, pow(l, twist));\n shape = mix(0., shape, mid);\n\n float proportion = clamp(u_proportion, 0., 1.);\n float exponent = mix(.25, 1., proportion * 2.);\n exponent = mix(exponent, 10., max(0., proportion * 2. - 1.));\n shape = pow(shape, exponent);\n\n float mixer = shape * u_colorsCount;\n vec4 gradient = u_colors[0];\n gradient.rgb *= gradient.a;\n \n float outerShape = 0.;\n for (int i = 1; i < ${swirlMeta.maxColorCount + 1}; i++) {\n if (i > int(u_colorsCount)) break;\n\n float m = clamp(mixer - float(i - 1), 0., 1.);\n float aa = fwidth(m);\n m = smoothstep(.5 - .5 * u_softness - aa, .5 + .5 * u_softness + aa, m);\n\n if (i == 1) {\n outerShape = m;\n }\n\n vec4 c = u_colors[i - 1];\n c.rgb *= c.a;\n gradient = mix(gradient, c, m);\n }\n\n float midAA = .1 * fwidth(pow(l, -twist));\n float outerMid = smoothstep(.2, .2 + midAA, pow(l, twist));\n outerShape = mix(0., outerShape, outerMid);\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 ${colorBandingFix}\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface SwirlUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n u_bandCount: number;\n u_twist: number;\n u_center: number;\n u_proportion: number;\n u_softness: number;\n u_noiseFrequency: number;\n u_noise: number;\n}\n\nexport interface SwirlParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colors?: string[];\n bandCount?: number;\n twist?: number;\n center?: number;\n proportion?: number;\n softness?: number;\n noiseFrequency?: number;\n noise?: number;\n}\n"],
5
- "mappings": ";;;;;AAEA,SAAS,kCAAsF;AAC/F,SAAS,cAAc,WAAW,WAAW,uBAAuB;AAE7D,MAAM,YAAY;AAAA,EACvB,eAAe;AACjB;AAgBO,MAAM,sBAA8B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAMnB,UAAU,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAU7C,0BAA0B;AAAA;AAAA;AAAA;AAAA,EAI1B,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,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,wBAgCa,UAAU,gBAAgB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA2B/C,eAAe;AAAA;AAAA;AAAA;AAAA;",
4
+ "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { simplexNoise, declarePI, rotation2, colorBandingFix } from '../shader-utils.js';\n\nexport const swirlMeta = {\n maxColorCount: 10,\n} as const;\n\n/**\n * Twisting radial bands\n *\n * Uniforms:\n * - u_colorBack (RGBA)\n * - u_colors (vec4[]), u_colorsCount (float used as integer)\n * - u_bandCount (float, used as int): number of sectors\n * - u_twist: sectors twist intensity (0 = linear)\n * - u_softness: color transition sharpness (0 = hard edge, 1 = smooth fade)\n * - u_noise, u_noiseFrequency: simplex noise distortion over the shape\n *\n */\n\n// language=GLSL\nexport const swirlFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colors[${swirlMeta.maxColorCount}];\nuniform float u_colorsCount;\nuniform float u_bandCount;\nuniform float u_twist;\nuniform float u_center;\nuniform float u_proportion;\nuniform float u_softness;\nuniform float u_noise;\nuniform float u_noiseFrequency;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n${simplexNoise}\n${rotation2}\n\nvoid main() {\n vec2 shape_uv = v_objectUV;\n\n float l = length(shape_uv);\n l = max(1e-4, l);\n \n float t = u_time;\n\n float angle = ceil(u_bandCount) * atan(shape_uv.y, shape_uv.x) + t;\n float angle_norm = angle / TWO_PI;\n\n float twist = 3. * clamp(u_twist, 0., 1.);\n float offset = pow(l, -twist) + angle_norm;\n\n float shape = fract(offset);\n shape = 1. - abs(2. * shape - 1.);\n shape += u_noise * snoise(15. * pow(u_noiseFrequency, 2.) * shape_uv);\n\n float mid = smoothstep(.2, .2 + .8 * u_center, pow(l, twist));\n shape = mix(0., shape, mid);\n\n float proportion = clamp(u_proportion, 0., 1.);\n float exponent = mix(.25, 1., proportion * 2.);\n exponent = mix(exponent, 10., max(0., proportion * 2. - 1.));\n shape = pow(shape, exponent);\n\n float mixer = shape * u_colorsCount;\n vec4 gradient = u_colors[0];\n gradient.rgb *= gradient.a;\n \n float outerShape = 0.;\n for (int i = 1; i < ${swirlMeta.maxColorCount + 1}; i++) {\n if (i > int(u_colorsCount)) break;\n\n float m = clamp(mixer - float(i - 1), 0., 1.);\n float aa = fwidth(m);\n m = smoothstep(.5 - .5 * u_softness - aa, .5 + .5 * u_softness + aa, m);\n\n if (i == 1) {\n outerShape = m;\n }\n\n vec4 c = u_colors[i - 1];\n c.rgb *= c.a;\n gradient = mix(gradient, c, m);\n }\n\n float midAA = .1 * fwidth(pow(l, -twist));\n float outerMid = smoothstep(.2, .2 + midAA, pow(l, twist));\n outerShape = mix(0., outerShape, outerMid);\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 ${colorBandingFix}\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface SwirlUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n u_bandCount: number;\n u_twist: number;\n u_center: number;\n u_proportion: number;\n u_softness: number;\n u_noiseFrequency: number;\n u_noise: number;\n}\n\nexport interface SwirlParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colors?: string[];\n bandCount?: number;\n twist?: number;\n center?: number;\n proportion?: number;\n softness?: number;\n noiseFrequency?: number;\n noise?: number;\n}\n"],
5
+ "mappings": ";;;;;AAEA,SAAS,kCAAsF;AAC/F,SAAS,cAAc,WAAW,WAAW,uBAAuB;AAE7D,MAAM,YAAY;AAAA,EACvB,eAAe;AACjB;AAgBO,MAAM,sBAA8B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAMnB,UAAU,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAU7C,0BAA0B;AAAA;AAAA;AAAA;AAAA,EAI1B,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,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,wBAiCa,UAAU,gBAAgB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA2B/C,eAAe;AAAA;AAAA;AAAA;AAAA;",
6
6
  "names": []
7
7
  }
@@ -82,11 +82,13 @@ void main() {
82
82
  } else if (u_shape < 1.5) {
83
83
  vec2 stripesShape_uv = uv * (2. * u_shapeScale);
84
84
  float f = fract(stripesShape_uv.y);
85
- shape = smoothstep(.0, .55, f) * smoothstep(1., .45, f);
85
+ shape = smoothstep(.0, .55, f) * (1.0 - smoothstep(.45, 1., f));
86
86
  shape += .48 * sign(proportion - .5) * pow(abs(proportion - .5), .5);
87
87
  } else {
88
88
  float shapeScaling = 5. * (1. - u_shapeScale);
89
- shape = smoothstep(.45 - shapeScaling, .55 + shapeScaling, 1. - uv.y + .3 * (proportion - .5));
89
+ float e0 = 0.45 - shapeScaling;
90
+ float e1 = 0.55 + shapeScaling;
91
+ shape = smoothstep(min(e0, e1), max(e0, e1), 1.0 - uv.y + 0.3 * (proportion - 0.5));
90
92
  }
91
93
 
92
94
  float mixer = shape * (u_colorsCount - 1.);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/shaders/warp.ts"],
4
- "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, rotation2, colorBandingFix } from '../shader-utils.js';\n\nexport const warpMeta = {\n maxColorCount: 10,\n} as const;\n\n/**\n * Iterative layered + swirl-based distortion applied over different layouts (shapes)\n *\n * Uniforms:\n * - u_colors (vec4[]), u_colorsCount (float used as integer)\n * - u_proportion: (0..1) blend point between 2 colors (0.5 = equal distribution)\n * - u_softness: color transition sharpness (0 = hard edge, 1 = smooth fade)\n * - u_shape (float used as integer):\n * ---- 0: checks\n * ---- 1: stripes\n * ---- 2: 2x halves of canvas (mapping the canvas height regardless of resolution)\n * - u_shapeScale: the scale of layouts (underlying shapes)\n * - u_distortion: value noise distortion over the UV coordinate\n * - u_swirl, u_swirlIterations: swirly distortion (layering curves effect)\n *\n * - u_noiseTexture (sampler2D): pre-computed randomizer source\n *\n */\n\n// language=GLSL\nexport const warpFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\nuniform float u_scale;\n\nuniform sampler2D u_noiseTexture;\n\nuniform vec4 u_colors[${warpMeta.maxColorCount}];\nuniform float u_colorsCount;\nuniform float u_proportion;\nuniform float u_softness;\nuniform float u_shape;\nuniform float u_shapeScale;\nuniform float u_distortion;\nuniform float u_swirl;\nuniform float u_swirlIterations;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n${rotation2}\nfloat randomG(vec2 p) {\n vec2 uv = floor(p) / 100. + .5;\n return texture(u_noiseTexture, fract(uv)).g;\n}\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = randomG(i);\n float b = randomG(i + vec2(1.0, 0.0));\n float c = randomG(i + vec2(0.0, 1.0));\n float d = randomG(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\n\nvoid main() {\n vec2 uv = v_patternUV;\n uv *= .5;\n\n const float firstFrameOffset = 118.;\n float t = 0.0625 * (u_time + firstFrameOffset);\n\n float n1 = valueNoise(uv * 1. + t);\n float n2 = valueNoise(uv * 2. - t);\n float angle = n1 * TWO_PI;\n uv.x += 4. * u_distortion * n2 * cos(angle);\n uv.y += 4. * u_distortion * n2 * sin(angle);\n\n float swirl = u_swirl;\n for (int i = 1; i <= 20; i++) {\n if (i >= int(u_swirlIterations)) break;\n float iFloat = float(i);\n// swirl *= (1. - smoothstep(.0, .25, length(fwidth(uv))));\n uv.x += swirl / iFloat * cos(t + iFloat * 1.5 * uv.y);\n uv.y += swirl / iFloat * cos(t + iFloat * 1. * uv.x);\n }\n\n float proportion = clamp(u_proportion, 0., 1.);\n\n float shape = 0.;\n if (u_shape < .5) {\n vec2 checksShape_uv = uv * (.5 + 3.5 * u_shapeScale);\n shape = .5 + .5 * sin(checksShape_uv.x) * cos(checksShape_uv.y);\n shape += .48 * sign(proportion - .5) * pow(abs(proportion - .5), .5);\n } else if (u_shape < 1.5) {\n vec2 stripesShape_uv = uv * (2. * u_shapeScale);\n float f = fract(stripesShape_uv.y);\n shape = smoothstep(.0, .55, f) * smoothstep(1., .45, f);\n shape += .48 * sign(proportion - .5) * pow(abs(proportion - .5), .5);\n } else {\n float shapeScaling = 5. * (1. - u_shapeScale);\n shape = smoothstep(.45 - shapeScaling, .55 + shapeScaling, 1. - uv.y + .3 * (proportion - .5));\n }\n\n float mixer = shape * (u_colorsCount - 1.);\n vec4 gradient = u_colors[0];\n gradient.rgb *= gradient.a;\n float aa = fwidth(shape);\n for (int i = 1; i < ${warpMeta.maxColorCount}; i++) {\n if (i >= int(u_colorsCount)) break;\n float m = clamp(mixer - float(i - 1), 0.0, 1.0);\n\n float localMixerStart = floor(m);\n float softness = .5 * u_softness + fwidth(m);\n float smoothed = smoothstep(max(0., .5 - softness - aa), min(1., .5 + softness + aa), m - localMixerStart);\n float stepped = localMixerStart + smoothed;\n\n m = mix(stepped, m, u_softness);\n\n vec4 c = u_colors[i];\n c.rgb *= c.a;\n gradient = mix(gradient, c, m);\n }\n\n vec3 color = gradient.rgb;\n float opacity = gradient.a;\n\n ${colorBandingFix}\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface WarpUniforms extends ShaderSizingUniforms {\n u_colors: vec4[];\n u_colorsCount: number;\n u_proportion: number;\n u_softness: number;\n u_shape: (typeof WarpPatterns)[WarpPattern];\n u_shapeScale: number;\n u_distortion: number;\n u_swirl: number;\n u_swirlIterations: number;\n u_noiseTexture?: HTMLImageElement;\n}\n\nexport interface WarpParams extends ShaderSizingParams, ShaderMotionParams {\n colors?: string[];\n rotation?: number;\n proportion?: number;\n softness?: number;\n shape?: WarpPattern;\n shapeScale?: number;\n distortion?: number;\n swirl?: number;\n swirlIterations?: number;\n}\n\nexport const WarpPatterns = {\n checks: 0,\n stripes: 1,\n edge: 2,\n} as const;\n\nexport type WarpPattern = keyof typeof WarpPatterns;\n"],
5
- "mappings": ";;;;;AAEA,SAAS,kCAAsF;AAC/F,SAAS,WAAW,WAAW,uBAAuB;AAE/C,MAAM,WAAW;AAAA,EACtB,eAAe;AACjB;AAsBO,MAAM,qBAA6B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAQlB,SAAS,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAU5C,0BAA0B;AAAA;AAAA;AAAA;AAAA,EAI1B,SAAS;AAAA,EACT,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,wBA8Da,SAAS,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAmB1C,eAAe;AAAA;AAAA;AAAA;AAAA;AA+BZ,MAAM,eAAe;AAAA,EAC1B,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,MAAM;AACR;",
4
+ "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, rotation2, colorBandingFix } from '../shader-utils.js';\n\nexport const warpMeta = {\n maxColorCount: 10,\n} as const;\n\n/**\n * Iterative layered + swirl-based distortion applied over different layouts (shapes)\n *\n * Uniforms:\n * - u_colors (vec4[]), u_colorsCount (float used as integer)\n * - u_proportion: (0..1) blend point between 2 colors (0.5 = equal distribution)\n * - u_softness: color transition sharpness (0 = hard edge, 1 = smooth fade)\n * - u_shape (float used as integer):\n * ---- 0: checks\n * ---- 1: stripes\n * ---- 2: 2x halves of canvas (mapping the canvas height regardless of resolution)\n * - u_shapeScale: the scale of layouts (underlying shapes)\n * - u_distortion: value noise distortion over the UV coordinate\n * - u_swirl, u_swirlIterations: swirly distortion (layering curves effect)\n *\n * - u_noiseTexture (sampler2D): pre-computed randomizer source\n *\n */\n\n// language=GLSL\nexport const warpFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\nuniform float u_scale;\n\nuniform sampler2D u_noiseTexture;\n\nuniform vec4 u_colors[${warpMeta.maxColorCount}];\nuniform float u_colorsCount;\nuniform float u_proportion;\nuniform float u_softness;\nuniform float u_shape;\nuniform float u_shapeScale;\nuniform float u_distortion;\nuniform float u_swirl;\nuniform float u_swirlIterations;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n${rotation2}\nfloat randomG(vec2 p) {\n vec2 uv = floor(p) / 100. + .5;\n return texture(u_noiseTexture, fract(uv)).g;\n}\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = randomG(i);\n float b = randomG(i + vec2(1.0, 0.0));\n float c = randomG(i + vec2(0.0, 1.0));\n float d = randomG(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\n\nvoid main() {\n vec2 uv = v_patternUV;\n uv *= .5;\n\n const float firstFrameOffset = 118.;\n float t = 0.0625 * (u_time + firstFrameOffset);\n\n float n1 = valueNoise(uv * 1. + t);\n float n2 = valueNoise(uv * 2. - t);\n float angle = n1 * TWO_PI;\n uv.x += 4. * u_distortion * n2 * cos(angle);\n uv.y += 4. * u_distortion * n2 * sin(angle);\n\n float swirl = u_swirl;\n for (int i = 1; i <= 20; i++) {\n if (i >= int(u_swirlIterations)) break;\n float iFloat = float(i);\n// swirl *= (1. - smoothstep(.0, .25, length(fwidth(uv))));\n uv.x += swirl / iFloat * cos(t + iFloat * 1.5 * uv.y);\n uv.y += swirl / iFloat * cos(t + iFloat * 1. * uv.x);\n }\n\n float proportion = clamp(u_proportion, 0., 1.);\n\n float shape = 0.;\n if (u_shape < .5) {\n vec2 checksShape_uv = uv * (.5 + 3.5 * u_shapeScale);\n shape = .5 + .5 * sin(checksShape_uv.x) * cos(checksShape_uv.y);\n shape += .48 * sign(proportion - .5) * pow(abs(proportion - .5), .5);\n } else if (u_shape < 1.5) {\n vec2 stripesShape_uv = uv * (2. * u_shapeScale);\n float f = fract(stripesShape_uv.y);\n shape = smoothstep(.0, .55, f) * (1.0 - smoothstep(.45, 1., f));\n shape += .48 * sign(proportion - .5) * pow(abs(proportion - .5), .5);\n } else {\n float shapeScaling = 5. * (1. - u_shapeScale);\n float e0 = 0.45 - shapeScaling;\n float e1 = 0.55 + shapeScaling;\n shape = smoothstep(min(e0, e1), max(e0, e1), 1.0 - uv.y + 0.3 * (proportion - 0.5));\n }\n\n float mixer = shape * (u_colorsCount - 1.);\n vec4 gradient = u_colors[0];\n gradient.rgb *= gradient.a;\n float aa = fwidth(shape);\n for (int i = 1; i < ${warpMeta.maxColorCount}; i++) {\n if (i >= int(u_colorsCount)) break;\n float m = clamp(mixer - float(i - 1), 0.0, 1.0);\n\n float localMixerStart = floor(m);\n float softness = .5 * u_softness + fwidth(m);\n float smoothed = smoothstep(max(0., .5 - softness - aa), min(1., .5 + softness + aa), m - localMixerStart);\n float stepped = localMixerStart + smoothed;\n\n m = mix(stepped, m, u_softness);\n\n vec4 c = u_colors[i];\n c.rgb *= c.a;\n gradient = mix(gradient, c, m);\n }\n\n vec3 color = gradient.rgb;\n float opacity = gradient.a;\n\n ${colorBandingFix}\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface WarpUniforms extends ShaderSizingUniforms {\n u_colors: vec4[];\n u_colorsCount: number;\n u_proportion: number;\n u_softness: number;\n u_shape: (typeof WarpPatterns)[WarpPattern];\n u_shapeScale: number;\n u_distortion: number;\n u_swirl: number;\n u_swirlIterations: number;\n u_noiseTexture?: HTMLImageElement;\n}\n\nexport interface WarpParams extends ShaderSizingParams, ShaderMotionParams {\n colors?: string[];\n rotation?: number;\n proportion?: number;\n softness?: number;\n shape?: WarpPattern;\n shapeScale?: number;\n distortion?: number;\n swirl?: number;\n swirlIterations?: number;\n}\n\nexport const WarpPatterns = {\n checks: 0,\n stripes: 1,\n edge: 2,\n} as const;\n\nexport type WarpPattern = keyof typeof WarpPatterns;\n"],
5
+ "mappings": ";;;;;AAEA,SAAS,kCAAsF;AAC/F,SAAS,WAAW,WAAW,uBAAuB;AAE/C,MAAM,WAAW;AAAA,EACtB,eAAe;AACjB;AAsBO,MAAM,qBAA6B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAQlB,SAAS,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAU5C,0BAA0B;AAAA;AAAA;AAAA;AAAA,EAI1B,SAAS;AAAA,EACT,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,wBAgEa,SAAS,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAmB1C,eAAe;AAAA;AAAA;AAAA;AAAA;AA+BZ,MAAM,eAAe;AAAA,EAC1B,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,MAAM;AACR;",
6
6
  "names": []
7
7
  }
@@ -36,9 +36,9 @@ float getUvFrame(vec2 uv) {
36
36
  float aay = 2. * fwidth(uv.y);
37
37
 
38
38
  float left = smoothstep(0., aax, uv.x);
39
- float right = smoothstep(1., 1. - aax, uv.x);
39
+ float right = 1.0 - smoothstep(1. - aax, 1., uv.x);
40
40
  float bottom = smoothstep(0., aay, uv.y);
41
- float top = smoothstep(1., 1. - aay, uv.y);
41
+ float top = 1.0 - smoothstep(1. - aay, 1., uv.y);
42
42
 
43
43
  return left * right * bottom * top;
44
44
  }
@@ -75,12 +75,12 @@ void main() {
75
75
  float causticNoise = getCausticNoise(patternUV + u_waves * vec2(1., -1.) * wavesNoise, 2. * t, 1.5);
76
76
 
77
77
  causticNoise += u_layering * getCausticNoise(patternUV + 2. * u_waves * vec2(1., -1.) * wavesNoise, 1.5 * t, 2.);
78
- causticNoise = pow(causticNoise, 2.);
78
+ causticNoise = causticNoise * causticNoise;
79
79
 
80
80
  float edgesDistortion = smoothstep(0., .1, imageUV.x);
81
81
  edgesDistortion *= smoothstep(0., .1, imageUV.y);
82
- edgesDistortion *= (smoothstep(1., 1.1, imageUV.x) + smoothstep(.95, .8, imageUV.x));
83
- edgesDistortion *= smoothstep(1., .9, imageUV.y);
82
+ edgesDistortion *= (smoothstep(1., 1.1, imageUV.x) + (1.0 - smoothstep(.8, .95, imageUV.x)));
83
+ edgesDistortion *= (1.0 - smoothstep(.9, 1., imageUV.y));
84
84
  edgesDistortion = mix(edgesDistortion, 1., u_edges);
85
85
 
86
86
  float causticNoiseDistortion = .02 * causticNoise * edgesDistortion;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/shaders/water.ts"],
4
- "sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, rotation2, simplexNoise } from '../shader-utils.js';\n\n/**\n * Mimicking water surface distortion with a combination of noises;\n * Can be applied over the texture or just be used as an animated pattern\n *\n * Uniforms:\n * - u_colorBack, u_colorHighlight (RGBA)\n * - u_size: pattern scale relative to the image\n * - u_caustic: power of caustic distortion\n * - u_layering: the power of 2nd layer of caustic distortion\n * - u_edges: caustic distortion power on the image edges\n * - u_waves: additional distortion based in Simplex noise, independent from caustic\n * - u_highlights: a coloring added over the image/background, following the caustic shape\n *\n */\n\n// language=GLSL\nexport const waterFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colorHighlight;\n\nuniform sampler2D u_image;\nuniform float u_imageAspectRatio;\n\nuniform float u_size;\nuniform float u_highlights;\nuniform float u_layering;\nuniform float u_edges;\nuniform float u_caustic;\nuniform float u_waves;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n${rotation2}\n${simplexNoise}\n\nfloat getUvFrame(vec2 uv) {\n float aax = 2. * fwidth(uv.x);\n float aay = 2. * fwidth(uv.y);\n\n float left = smoothstep(0., aax, uv.x);\n float right = smoothstep(1., 1. - aax, uv.x);\n float bottom = smoothstep(0., aay, uv.y);\n float top = smoothstep(1., 1. - aay, uv.y);\n\n return left * right * bottom * top;\n}\n\nmat2 rotate2D(float r) {\n return mat2(cos(r), sin(r), -sin(r), cos(r));\n}\n\nfloat getCausticNoise(vec2 uv, float t, float scale) {\n vec2 n = vec2(.1);\n vec2 N = vec2(.1);\n mat2 m = rotate2D(.5);\n for (int j = 0; j < 6; j++) {\n uv *= m;\n n *= m;\n vec2 q = uv * scale + float(j) + n + (.5 + .5 * float(j)) * (mod(float(j), 2.) - 1.) * t;\n n += sin(q);\n N += cos(q) / scale;\n scale *= 1.1;\n }\n return (N.x + N.y + 1.);\n}\n\nvoid main() {\n vec2 imageUV = v_imageUV;\n vec2 patternUV = v_imageUV - .5;\n patternUV = (patternUV * vec2(u_imageAspectRatio, 1.));\n patternUV /= (.01 + .09 * u_size);\n\n float t = u_time;\n\n float wavesNoise = snoise((.3 + .1 * sin(t)) * .1 * patternUV + vec2(0., .4 * t));\n\n float causticNoise = getCausticNoise(patternUV + u_waves * vec2(1., -1.) * wavesNoise, 2. * t, 1.5);\n\n causticNoise += u_layering * getCausticNoise(patternUV + 2. * u_waves * vec2(1., -1.) * wavesNoise, 1.5 * t, 2.);\n causticNoise = pow(causticNoise, 2.);\n\n float edgesDistortion = smoothstep(0., .1, imageUV.x);\n edgesDistortion *= smoothstep(0., .1, imageUV.y);\n edgesDistortion *= (smoothstep(1., 1.1, imageUV.x) + smoothstep(.95, .8, imageUV.x));\n edgesDistortion *= smoothstep(1., .9, imageUV.y);\n edgesDistortion = mix(edgesDistortion, 1., u_edges);\n\n float causticNoiseDistortion = .02 * causticNoise * edgesDistortion;\n\n float wavesDistortion = .1 * u_waves * wavesNoise;\n\n imageUV += vec2(wavesDistortion, -wavesDistortion);\n imageUV += (u_caustic * causticNoiseDistortion);\n\n float frame = getUvFrame(imageUV);\n\n vec4 image = texture(u_image, imageUV);\n vec4 backColor = u_colorBack;\n backColor.rgb *= backColor.a;\n\n vec3 color = mix(backColor.rgb, image.rgb, image.a * frame);\n float opacity = backColor.a + image.a * frame;\n\n causticNoise = max(-.2, causticNoise);\n\n float hightlight = .025 * u_highlights * causticNoise;\n hightlight *= u_colorHighlight.a;\n color = mix(color, u_colorHighlight.rgb, .05 * u_highlights * causticNoise);\n opacity += hightlight;\n\n color += hightlight * (.5 + .5 * wavesNoise);\n opacity += hightlight * (.5 + .5 * wavesNoise);\n\n opacity = clamp(opacity, 0., 1.);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface WaterUniforms extends ShaderSizingUniforms {\n u_image: HTMLImageElement | string | undefined;\n u_colorBack: [number, number, number, number];\n u_colorHighlight: [number, number, number, number];\n u_highlights: number;\n u_layering: number;\n u_edges: number;\n u_caustic: number;\n u_waves: number;\n u_size: number;\n}\n\nexport interface WaterParams extends ShaderSizingParams, ShaderMotionParams {\n image?: HTMLImageElement | string;\n colorBack?: string;\n colorHighlight?: string;\n highlights?: number;\n layering?: number;\n edges?: number;\n caustic?: number;\n waves?: number;\n size?: number;\n}\n"],
4
+ "sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, rotation2, simplexNoise } from '../shader-utils.js';\n\n/**\n * Mimicking water surface distortion with a combination of noises;\n * Can be applied over the texture or just be used as an animated pattern\n *\n * Uniforms:\n * - u_colorBack, u_colorHighlight (RGBA)\n * - u_size: pattern scale relative to the image\n * - u_caustic: power of caustic distortion\n * - u_layering: the power of 2nd layer of caustic distortion\n * - u_edges: caustic distortion power on the image edges\n * - u_waves: additional distortion based in Simplex noise, independent from caustic\n * - u_highlights: a coloring added over the image/background, following the caustic shape\n *\n */\n\n// language=GLSL\nexport const waterFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colorHighlight;\n\nuniform sampler2D u_image;\nuniform float u_imageAspectRatio;\n\nuniform float u_size;\nuniform float u_highlights;\nuniform float u_layering;\nuniform float u_edges;\nuniform float u_caustic;\nuniform float u_waves;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n${rotation2}\n${simplexNoise}\n\nfloat getUvFrame(vec2 uv) {\n float aax = 2. * fwidth(uv.x);\n float aay = 2. * fwidth(uv.y);\n\n float left = smoothstep(0., aax, uv.x);\n float right = 1.0 - smoothstep(1. - aax, 1., uv.x);\n float bottom = smoothstep(0., aay, uv.y);\n float top = 1.0 - smoothstep(1. - aay, 1., uv.y);\n\n return left * right * bottom * top;\n}\n\nmat2 rotate2D(float r) {\n return mat2(cos(r), sin(r), -sin(r), cos(r));\n}\n\nfloat getCausticNoise(vec2 uv, float t, float scale) {\n vec2 n = vec2(.1);\n vec2 N = vec2(.1);\n mat2 m = rotate2D(.5);\n for (int j = 0; j < 6; j++) {\n uv *= m;\n n *= m;\n vec2 q = uv * scale + float(j) + n + (.5 + .5 * float(j)) * (mod(float(j), 2.) - 1.) * t;\n n += sin(q);\n N += cos(q) / scale;\n scale *= 1.1;\n }\n return (N.x + N.y + 1.);\n}\n\nvoid main() {\n vec2 imageUV = v_imageUV;\n vec2 patternUV = v_imageUV - .5;\n patternUV = (patternUV * vec2(u_imageAspectRatio, 1.));\n patternUV /= (.01 + .09 * u_size);\n\n float t = u_time;\n\n float wavesNoise = snoise((.3 + .1 * sin(t)) * .1 * patternUV + vec2(0., .4 * t));\n\n float causticNoise = getCausticNoise(patternUV + u_waves * vec2(1., -1.) * wavesNoise, 2. * t, 1.5);\n\n causticNoise += u_layering * getCausticNoise(patternUV + 2. * u_waves * vec2(1., -1.) * wavesNoise, 1.5 * t, 2.);\n causticNoise = causticNoise * causticNoise;\n\n float edgesDistortion = smoothstep(0., .1, imageUV.x);\n edgesDistortion *= smoothstep(0., .1, imageUV.y);\n edgesDistortion *= (smoothstep(1., 1.1, imageUV.x) + (1.0 - smoothstep(.8, .95, imageUV.x)));\n edgesDistortion *= (1.0 - smoothstep(.9, 1., imageUV.y));\n edgesDistortion = mix(edgesDistortion, 1., u_edges);\n\n float causticNoiseDistortion = .02 * causticNoise * edgesDistortion;\n\n float wavesDistortion = .1 * u_waves * wavesNoise;\n\n imageUV += vec2(wavesDistortion, -wavesDistortion);\n imageUV += (u_caustic * causticNoiseDistortion);\n\n float frame = getUvFrame(imageUV);\n\n vec4 image = texture(u_image, imageUV);\n vec4 backColor = u_colorBack;\n backColor.rgb *= backColor.a;\n\n vec3 color = mix(backColor.rgb, image.rgb, image.a * frame);\n float opacity = backColor.a + image.a * frame;\n\n causticNoise = max(-.2, causticNoise);\n\n float hightlight = .025 * u_highlights * causticNoise;\n hightlight *= u_colorHighlight.a;\n color = mix(color, u_colorHighlight.rgb, .05 * u_highlights * causticNoise);\n opacity += hightlight;\n\n color += hightlight * (.5 + .5 * wavesNoise);\n opacity += hightlight * (.5 + .5 * wavesNoise);\n\n opacity = clamp(opacity, 0., 1.);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface WaterUniforms extends ShaderSizingUniforms {\n u_image: HTMLImageElement | string | undefined;\n u_colorBack: [number, number, number, number];\n u_colorHighlight: [number, number, number, number];\n u_highlights: number;\n u_layering: number;\n u_edges: number;\n u_caustic: number;\n u_waves: number;\n u_size: number;\n}\n\nexport interface WaterParams extends ShaderSizingParams, ShaderMotionParams {\n image?: HTMLImageElement | string;\n colorBack?: string;\n colorHighlight?: string;\n highlights?: number;\n layering?: number;\n edges?: number;\n caustic?: number;\n waves?: number;\n size?: number;\n}\n"],
5
5
  "mappings": ";;;;;AACA,SAAS,kCAAsF;AAC/F,SAAS,WAAW,WAAW,oBAAoB;AAkB5C,MAAM,sBAA8B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBzC,0BAA0B;AAAA;AAAA;AAAA;AAAA,EAI1B,SAAS;AAAA,EACT,SAAS;AAAA,EACT,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
6
6
  "names": []
7
7
  }
@@ -42,7 +42,9 @@ void main() {
42
42
 
43
43
  float aa = .0001 + fwidth(shape);
44
44
  float dc = 1. - clamp(u_proportion, 0., 1.);
45
- float res = smoothstep(dc - u_softness - aa, dc + u_softness + aa, shape);
45
+ float e0 = dc - u_softness - aa;
46
+ float e1 = dc + u_softness + aa;
47
+ float res = smoothstep(min(e0, e1), max(e0, e1), shape);
46
48
 
47
49
  vec3 fgColor = u_colorFront.rgb * u_colorFront.a;
48
50
  float fgOpacity = u_colorFront.a;