@toriistudio/shader-ui 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -30,7 +30,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
+ EFECTO_ASCII_COMPONENT_DEFAULTS: () => EFECTO_ASCII_COMPONENT_DEFAULTS,
34
+ EFECTO_ASCII_POST_PROCESSING_DEFAULTS: () => EFECTO_ASCII_POST_PROCESSING_DEFAULTS,
35
+ Efecto: () => Efecto,
33
36
  FractalFlower: () => FractalFlower,
37
+ MenuGlitch: () => MenuGlitch,
34
38
  OranoParticles: () => OranoParticles,
35
39
  ShaderArt: () => ShaderArt
36
40
  });
@@ -51,13 +55,15 @@ function useScene({
51
55
  onCreate,
52
56
  onRender,
53
57
  onResize,
54
- deps
58
+ deps,
59
+ manualRender = false
55
60
  } = {}) {
56
61
  const containerRef = (0, import_react.useRef)(null);
57
62
  const contextRef = (0, import_react.useRef)(null);
58
63
  const onCreateRef = (0, import_react.useRef)(onCreate);
59
64
  const onRenderRef = (0, import_react.useRef)(onRender);
60
65
  const onResizeRef = (0, import_react.useRef)(onResize);
66
+ const sizeRef = (0, import_react.useRef)({ width: 0, height: 0 });
61
67
  (0, import_react.useEffect)(() => {
62
68
  onCreateRef.current = onCreate;
63
69
  }, [onCreate]);
@@ -72,43 +78,90 @@ function useScene({
72
78
  const container = containerRef.current;
73
79
  if (!container) return;
74
80
  const resolvedPixelRatio = pixelRatio ?? (typeof window !== "undefined" ? Math.min(window.devicePixelRatio, 2) : 1);
81
+ const initialWidth = container.clientWidth || 1;
82
+ const initialHeight = container.clientHeight || 1;
83
+ sizeRef.current = { width: initialWidth, height: initialHeight };
75
84
  const renderer = new THREE.WebGLRenderer({
76
85
  alpha: true,
77
86
  antialias: true,
78
87
  ...rendererOptions
79
88
  });
89
+ renderer.autoClear = false;
90
+ renderer.autoClearColor = true;
91
+ renderer.autoClearDepth = true;
92
+ renderer.autoClearStencil = true;
80
93
  renderer.setPixelRatio(resolvedPixelRatio);
81
- renderer.setSize(container.clientWidth, container.clientHeight, false);
94
+ renderer.setSize(initialWidth, initialHeight, false);
82
95
  renderer.setClearColor(0, 0);
83
96
  renderer.domElement.style.width = "100%";
84
97
  renderer.domElement.style.height = "100%";
85
98
  container.appendChild(renderer.domElement);
86
99
  const scene = new THREE.Scene();
87
- const camera = new THREE.PerspectiveCamera(
88
- cameraOptions?.fov ?? 55,
89
- container.clientWidth / Math.max(1, container.clientHeight),
90
- cameraOptions?.near ?? 0.1,
91
- cameraOptions?.far ?? 500
92
- );
93
- const [x = 0, y = 0, z = 15] = cameraOptions?.position ?? [];
94
- camera.position.set(x, y, z);
100
+ let camera;
101
+ if (cameraOptions?.type === "orthographic") {
102
+ const halfWidth = initialWidth / 2;
103
+ const halfHeight = initialHeight / 2;
104
+ const orthoCamera = new THREE.OrthographicCamera(
105
+ -halfWidth,
106
+ halfWidth,
107
+ halfHeight,
108
+ -halfHeight,
109
+ cameraOptions?.near ?? 0.1,
110
+ cameraOptions?.far ?? 1e3
111
+ );
112
+ const [, , z = 10] = cameraOptions?.position ?? [];
113
+ orthoCamera.position.set(0, 0, z);
114
+ camera = orthoCamera;
115
+ } else {
116
+ const perspectiveCamera = new THREE.PerspectiveCamera(
117
+ cameraOptions?.fov ?? 55,
118
+ initialWidth / Math.max(1, initialHeight),
119
+ cameraOptions?.near ?? 0.1,
120
+ cameraOptions?.far ?? 500
121
+ );
122
+ const [x = 0, y = 0, z = 15] = cameraOptions?.position ?? [];
123
+ perspectiveCamera.position.set(x, y, z);
124
+ camera = perspectiveCamera;
125
+ }
95
126
  const clock = new THREE.Clock();
96
- const context = { renderer, scene, camera, clock };
127
+ const context = {
128
+ renderer,
129
+ scene,
130
+ camera,
131
+ clock,
132
+ size: { ...sizeRef.current }
133
+ };
97
134
  contextRef.current = context;
98
135
  const teardownCreate = onCreateRef.current?.(context);
136
+ let elapsedTime = 0;
99
137
  let animationFrameId = 0;
100
138
  const renderLoop = () => {
101
- onRenderRef.current?.(context);
102
- renderer.render(scene, camera);
139
+ const delta = clock.getDelta();
140
+ elapsedTime += delta;
141
+ context.size = { ...sizeRef.current };
142
+ onRenderRef.current?.(context, delta, elapsedTime);
143
+ if (!manualRender) {
144
+ renderer.render(scene, camera);
145
+ }
103
146
  animationFrameId = requestAnimationFrame(renderLoop);
104
147
  };
105
148
  animationFrameId = requestAnimationFrame(renderLoop);
106
149
  const resizeObserver = new ResizeObserver(() => {
107
- const width = container.clientWidth;
108
- const height = container.clientHeight;
150
+ const width = container.clientWidth || 1;
151
+ const height = container.clientHeight || 1;
109
152
  renderer.setSize(width, height, false);
110
- camera.aspect = width / Math.max(1, height);
111
- camera.updateProjectionMatrix();
153
+ sizeRef.current = { width, height };
154
+ if (camera instanceof THREE.PerspectiveCamera) {
155
+ camera.aspect = width / Math.max(1, height);
156
+ camera.updateProjectionMatrix();
157
+ } else if (camera instanceof THREE.OrthographicCamera) {
158
+ camera.left = -width / 2;
159
+ camera.right = width / 2;
160
+ camera.top = height / 2;
161
+ camera.bottom = -height / 2;
162
+ camera.updateProjectionMatrix();
163
+ }
164
+ context.size = { ...sizeRef.current };
112
165
  onResizeRef.current?.(context, { width, height });
113
166
  });
114
167
  resizeObserver.observe(container);
@@ -168,15 +221,14 @@ function ShaderArt({
168
221
  assetsRef.current = null;
169
222
  };
170
223
  }, []);
171
- const handleRender = (0, import_react2.useCallback)((context) => {
172
- shaderUniformsRef.current.iTime.value = context.clock.getElapsedTime();
173
- const canvas = context.renderer.domElement;
174
- shaderUniformsRef.current.iResolution.value.set(
175
- canvas.width,
176
- canvas.height,
177
- 1
178
- );
179
- }, []);
224
+ const handleRender = (0, import_react2.useCallback)(
225
+ (context, _delta, elapsedTime) => {
226
+ shaderUniformsRef.current.iTime.value = elapsedTime;
227
+ const { width: width2, height: height2 } = context.size;
228
+ shaderUniformsRef.current.iResolution.value.set(width2, height2, 1);
229
+ },
230
+ []
231
+ );
180
232
  const { containerRef } = useScene({
181
233
  onCreate: handleCreate,
182
234
  onRender: handleRender
@@ -318,7 +370,12 @@ function FractalFlower({
318
370
  const points = new THREE3.Points(geometry, material);
319
371
  points.frustumCulled = false;
320
372
  scene.add(points);
321
- assetsRef.current = { points, geometry, material, uniforms: uniformValues };
373
+ assetsRef.current = {
374
+ points,
375
+ geometry,
376
+ material,
377
+ uniforms: uniformValues
378
+ };
322
379
  return () => {
323
380
  scene.remove(points);
324
381
  geometry.dispose();
@@ -326,25 +383,29 @@ function FractalFlower({
326
383
  assetsRef.current = null;
327
384
  };
328
385
  },
329
- [attributes.eValues, attributes.kValues, attributes.positions, attributes.rotations]
386
+ [
387
+ attributes.eValues,
388
+ attributes.kValues,
389
+ attributes.positions,
390
+ attributes.rotations
391
+ ]
330
392
  );
331
393
  const handleRender = (0, import_react3.useCallback)(
332
- (context) => {
394
+ (context, delta, elapsedTime) => {
333
395
  const assets = assetsRef.current;
334
396
  if (!assets) return;
335
397
  const uniforms = assets.uniforms;
336
- uniforms.uTime.value = context.clock.getElapsedTime();
337
- const canvas = context.renderer.domElement;
338
- uniforms.uResolution.value.set(canvas.width, canvas.height);
398
+ uniforms.uTime.value = elapsedTime;
399
+ const { width: width2, height: height2 } = context.size;
400
+ uniforms.uResolution.value.set(width2, height2);
339
401
  const basePointSize = Math.max(
340
402
  2,
341
- canvas.height / 400 * (uniforms.uPetalRadius.value * 32)
403
+ height2 / 400 * (uniforms.uPetalRadius.value * 32)
342
404
  );
343
405
  const pointSize = Math.min(basePointSize, 6);
344
406
  uniforms.uPointSize.value = pointSize;
345
407
  const morph = morphRef.current;
346
408
  if (Math.abs(morph.target - morph.progress) > 1e-3) {
347
- const delta = context.clock.getDelta();
348
409
  const direction = Math.sign(morph.target - morph.progress);
349
410
  morph.progress = clamp(
350
411
  morph.progress + direction * delta * MORPH_SPEED,
@@ -495,11 +556,14 @@ function OranoParticles({
495
556
  particlesRef.current = null;
496
557
  };
497
558
  }, []);
498
- const handleRender = (0, import_react4.useCallback)((context) => {
499
- const assets = particlesRef.current;
500
- if (!assets) return;
501
- assets.uniforms.uTime.value = context.clock.getElapsedTime();
502
- }, []);
559
+ const handleRender = (0, import_react4.useCallback)(
560
+ (_context, _delta, elapsedTime) => {
561
+ const assets = particlesRef.current;
562
+ if (!assets) return;
563
+ assets.uniforms.uTime.value = elapsedTime;
564
+ },
565
+ []
566
+ );
503
567
  const { containerRef } = useScene({
504
568
  onCreate: handleCreate,
505
569
  onRender: handleRender
@@ -558,9 +622,1502 @@ function OranoParticles({
558
622
  }
559
623
  );
560
624
  }
625
+
626
+ // src/components/MenuGlitch.tsx
627
+ var import_gsap = require("gsap");
628
+ var import_react5 = require("react");
629
+ var THREE5 = __toESM(require("three"));
630
+
631
+ // src/shaders/menu-glitch/fragment.glsl
632
+ var fragment_default4 = "#define M_PI 3.1415926535897932384626433832795\n\nuniform sampler2D tDiffuse;\nuniform sampler2D tGradient;\n\nuniform float uTime;\n\nuniform float uTileProgressVertical;\nuniform float uTileProgressHorizontal;\nuniform float uTileAmplitude;\nuniform vec2 uTileFrequency;\nuniform vec2 uTileOffset;\n\nuniform float uWaveProgress;\nuniform float uWaveAmplitude;\nuniform vec2 uWaveStrength;\n\nuniform float uGradientProgress;\nuniform float uGradientOffset;\nuniform float uGradientAmplitude;\n\nuniform float uBlueProgress;\nuniform float uBlueAmplitude;\n\nuniform float uWhiteTileChances;\nuniform float uWhiteTileFrequency;\nuniform float uWhiteTileStrength;\n\nuniform float uSaturation;\n\nvarying vec2 vUv;\n\n\n// --------------------------------------------------\n// Utilities\n// --------------------------------------------------\n\nfloat random(in vec2 st) {\n return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);\n}\n\nvec2 getTileCoord(vec2 pos, vec2 frequency) {\n vec2 coord = vec2(\n floor(pos.x * frequency.x),\n floor(pos.y * frequency.y)\n );\n\n coord /= frequency;\n return coord;\n}\n\nfloat toSin(float value) {\n return (sin((value - 0.5) * M_PI) + 1.0) * 0.5;\n}\n\n\n// --------------------------------------------------\n// Main\n// --------------------------------------------------\n\nvoid main() {\n\n // ------------------------\n // Tiles\n // ------------------------\n vec2 tileCoord1 = getTileCoord(vUv, uTileFrequency * 1.8942);\n vec2 tileCoord2 = getTileCoord(vUv, uTileFrequency * 1.0);\n vec2 tileCoord3 = getTileCoord(vUv, uTileFrequency * 2.1245);\n\n vec2 tileCoord = tileCoord2 +\n step(random(tileCoord1), 0.5) * (tileCoord3 - tileCoord2);\n\n float tileRandom = random(tileCoord);\n float tileAngle = tileRandom * M_PI * 2.0;\n\n vec2 tileOffset = vec2(sin(tileAngle), cos(tileAngle)) * uTileOffset;\n\n float tileProgress = 1.0 -\n (distance(tileCoord.y, uTileProgressVertical) / (uTileAmplitude * 0.5));\n\n tileProgress = clamp(tileProgress, 0.0, 1.0);\n\n float tileProgressHorizontal = 1.0 -\n (distance(tileCoord.x, uTileProgressHorizontal) / (uTileAmplitude * 0.5));\n tileProgressHorizontal = clamp(tileProgressHorizontal, 0.0, 1.0);\n\n // ------------------------\n // Wave\n // ------------------------\n float waveProgress = 1.0 -\n (distance(vUv.x, uWaveProgress) / (uWaveAmplitude * 0.5));\n\n waveProgress = clamp(waveProgress, 0.0, 1.0);\n\n vec2 waveOffset = toSin(waveProgress) * uWaveStrength;\n\n\n // ------------------------\n // Gradient\n // ------------------------\n float gradientProgress = (tileCoord.x - uGradientProgress) / uGradientAmplitude;\n gradientProgress += (tileRandom - 0.5) * uGradientOffset;\n\n vec4 gradientColor = texture2D(\n tGradient,\n vec2(clamp(gradientProgress, 0.0, 1.0), 0.0)\n );\n\n\n // ------------------------\n // Blue tint\n // ------------------------\n float blueProgress = (tileCoord.x - uBlueProgress) / uBlueAmplitude;\n blueProgress += tileOffset.x;\n blueProgress = clamp(blueProgress, 0.0, 1.0);\n\n\n // ------------------------\n // White flickering tiles\n // ------------------------\n float whiteTileProgress =\n sin(uTime * uWhiteTileFrequency + tileRandom * M_PI * 2.0) * 0.5 + 0.5;\n\n whiteTileProgress =\n clamp(whiteTileProgress - (1.0 - uWhiteTileChances), 0.0, 1.0) *\n (1.0 / uWhiteTileChances) *\n uWhiteTileStrength;\n\n\n // ------------------------\n // Final color pipeline\n // ------------------------\n vec2 uv = vUv;\n\n // Apply tile offset\n uv += tileOffset * tileProgress;\n\n // Apply waves (optional)\n // uv += waveOffset;\n\n // Repeat UV\n uv = mod(uv, vec2(1.0));\n\n // Base color removed so overlay only contains glitch elements\n vec4 color = vec4(0.0);\n\n // Add gradient\n color.rgb += gradientColor.rgb * gradientColor.a * tileProgressHorizontal;\n\n // Add white flicker effect\n color.rgb += vec3(whiteTileProgress) * tileProgressHorizontal;\n\n // Blue tone shift\n vec3 blueColor = vec3((color.r + color.g + color.b) / 3.0) *\n vec3(0.3, 0.5, 1.0);\n\n color.rgb = mix(color.rgb, blueColor, vec3(blueProgress));\n\n // Saturation\n color.rgb *= uSaturation;\n\n float effectAlpha =\n clamp(tileProgressHorizontal * (tileProgress * 0.6 + whiteTileProgress + gradientColor.a), 0.0, 1.0);\n\n // Output\n gl_FragColor = vec4(color.rgb, effectAlpha);\n}\n";
633
+
634
+ // src/shaders/menu-glitch/vertex.glsl
635
+ var vertex_default4 = "varying vec2 vUv;\n\nvoid main() {\n vUv = uv;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n";
636
+
637
+ // src/components/MenuGlitch.tsx
638
+ var import_jsx_runtime4 = require("react/jsx-runtime");
639
+ var GRADIENT_DATA_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAABCAYAAAAxWXB3AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTM4IDc5LjE1OTgyNCwgMjAxNi8wOS8xNC0wMTowOTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTcgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RTVBRkY1OTJDREMyMTFFODhGRjFFRjgxRjM2QjM2MDMiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RTVBRkY1OTNDREMyMTFFODhGRjFFRjgxRjM2QjM2MDMiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpFNUFGRjU5MENEQzIxMUU4OEZGMUVGODFGMzZCMzYwMyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpFNUFGRjU5MUNEQzIxMUU4OEZGMUVGODFGMzZCMzYwMyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pksi3ywAAADMSURBVHjarFDLDoMwDDMtUDjtME3TtMcn7f9/hngBCoTSTjsMydixnVZqxTcAX51Quwsad1M80PonWvdCUA7+rrii82eEulcG+hoTdwsrQtRB0UZu3KxrN8OPqIBKgRHxI8qaUTD1RuY2p90V+hOjmWix2uSSeGLPKrDdKc5GHzqZLPWY7XEG+F1PLBktpiuzf8ilkMvqcZfLLqf1ltnu7rLh4G88ZPxh4uUOmjMYO3aPqz8Yve0tGiaDduNr/xn8cWbBY8HLzQd8BBgAgOx+ERYDbIEAAAAASUVORK5CYII=";
640
+ var BASE_TRANSITION_PROGRESS = 0.5;
641
+ var SHOW_BASE_DURATION = 2.72;
642
+ var HIDE_BASE_DURATION = 1.8;
643
+ var SHOW_PRIMARY_DURATION = 1.1;
644
+ var MIN_GLITCH_DURATION = 0.25;
645
+ function MenuGlitch({
646
+ settings,
647
+ onShowDone,
648
+ onHideDone,
649
+ className,
650
+ style,
651
+ width,
652
+ height,
653
+ ...divProps
654
+ }) {
655
+ const diffuseTexture = (0, import_react5.useMemo)(() => createDiffuseTexture(), []);
656
+ const gradientTextureRef = (0, import_react5.useRef)(new THREE5.Texture());
657
+ const sanitizedValues = (0, import_react5.useMemo)(() => {
658
+ const clamp2 = THREE5.MathUtils.clamp;
659
+ return {
660
+ tileAmplitude: Math.max(0.1, settings.tileAmplitude),
661
+ planeScale: Math.max(0.1, settings.planeScale ?? 1),
662
+ tileOffsetX: settings.tileOffset.x,
663
+ tileOffsetY: settings.tileOffset.y,
664
+ tileFrequencyX: Math.max(0.1, settings.tileFrequency.x),
665
+ tileFrequencyY: Math.max(0.1, settings.tileFrequency.y),
666
+ gradientAmplitude: Math.max(0.05, settings.gradientAmplitude),
667
+ gradientOffset: settings.gradientOffset,
668
+ gradientProgress: settings.gradientProgress ?? 1,
669
+ blueAmplitude: Math.max(0.05, settings.blueAmplitude),
670
+ blueProgress: settings.blueProgress ?? 1,
671
+ waveAmplitude: Math.max(0.05, settings.waveAmplitude),
672
+ waveProgress: settings.waveProgress ?? 0.5,
673
+ waveStrengthX: settings.waveStrength.x,
674
+ waveStrengthY: settings.waveStrength.y,
675
+ whiteTileChances: clamp2(settings.whiteTileChances, 0.05, 1),
676
+ whiteTileFrequency: Math.max(0.01, settings.whiteTileFrequency),
677
+ whiteTileStrength: Math.max(0, settings.whiteTileStrength),
678
+ saturation: Math.max(0, settings.saturation),
679
+ duration: Math.max(
680
+ MIN_GLITCH_DURATION,
681
+ settings.duration ?? SHOW_PRIMARY_DURATION
682
+ ),
683
+ debug: settings.debug
684
+ };
685
+ }, [settings]);
686
+ const sanitizedRef = (0, import_react5.useRef)(sanitizedValues);
687
+ sanitizedRef.current = sanitizedValues;
688
+ const sizeRef = (0, import_react5.useRef)({ width: 0, height: 0 });
689
+ const assetsRef = (0, import_react5.useRef)(null);
690
+ const animationVisibleRef = (0, import_react5.useRef)(false);
691
+ const shaderUniforms = (0, import_react5.useRef)({
692
+ tDiffuse: { value: diffuseTexture },
693
+ tGradient: { value: gradientTextureRef.current },
694
+ uTime: { value: 0 },
695
+ uTileProgressVertical: { value: -0.5 },
696
+ uTileProgressHorizontal: { value: -0.5 },
697
+ uTileAmplitude: { value: settings.tileAmplitude },
698
+ uTileOffset: {
699
+ value: new THREE5.Vector2(settings.tileOffset.x, settings.tileOffset.y)
700
+ },
701
+ uTileFrequency: {
702
+ value: new THREE5.Vector2(
703
+ settings.tileFrequency.x,
704
+ settings.tileFrequency.y
705
+ )
706
+ },
707
+ uWaveProgress: { value: settings.waveProgress },
708
+ uWaveAmplitude: { value: settings.waveAmplitude },
709
+ uWaveStrength: {
710
+ value: new THREE5.Vector2(
711
+ settings.waveStrength.x,
712
+ settings.waveStrength.y
713
+ )
714
+ },
715
+ uGradientProgress: { value: settings.gradientProgress },
716
+ uGradientOffset: { value: settings.gradientOffset },
717
+ uGradientAmplitude: { value: settings.gradientAmplitude },
718
+ uBlueProgress: { value: settings.blueProgress },
719
+ uBlueAmplitude: { value: settings.blueAmplitude },
720
+ uWhiteTileChances: { value: settings.whiteTileChances },
721
+ uWhiteTileFrequency: { value: settings.whiteTileFrequency },
722
+ uWhiteTileStrength: { value: settings.whiteTileStrength },
723
+ uSaturation: { value: settings.saturation }
724
+ });
725
+ (0, import_react5.useEffect)(() => {
726
+ let disposed = false;
727
+ const loader = new THREE5.TextureLoader();
728
+ loader.load(
729
+ GRADIENT_DATA_URL,
730
+ (texture) => {
731
+ if (disposed) return;
732
+ texture.wrapS = THREE5.ClampToEdgeWrapping;
733
+ texture.wrapT = THREE5.ClampToEdgeWrapping;
734
+ texture.magFilter = THREE5.LinearFilter;
735
+ texture.minFilter = THREE5.LinearFilter;
736
+ texture.colorSpace = THREE5.SRGBColorSpace;
737
+ gradientTextureRef.current = texture;
738
+ shaderUniforms.current.tGradient.value = texture;
739
+ },
740
+ void 0,
741
+ () => {
742
+ if (disposed) return;
743
+ gradientTextureRef.current = new THREE5.Texture();
744
+ shaderUniforms.current.tGradient.value = gradientTextureRef.current;
745
+ }
746
+ );
747
+ return () => {
748
+ disposed = true;
749
+ };
750
+ }, []);
751
+ const updateMeshScale = (0, import_react5.useCallback)(() => {
752
+ const assets = assetsRef.current;
753
+ if (!assets) return;
754
+ const base = Math.max(
755
+ sizeRef.current.width || 1,
756
+ sizeRef.current.height || 1
757
+ );
758
+ const scale = base * sanitizedRef.current.planeScale;
759
+ assets.mesh.scale.set(scale, scale, 1);
760
+ }, []);
761
+ const updateVisibility = (0, import_react5.useCallback)(() => {
762
+ const mesh = assetsRef.current?.mesh;
763
+ if (!mesh) return;
764
+ mesh.visible = sanitizedRef.current.debug || animationVisibleRef.current;
765
+ }, []);
766
+ const handleCreate = (0, import_react5.useCallback)(
767
+ ({ scene, size }) => {
768
+ const geometry = new THREE5.PlaneGeometry(1, 1);
769
+ const material = new THREE5.ShaderMaterial({
770
+ vertexShader: vertex_default4,
771
+ fragmentShader: fragment_default4,
772
+ uniforms: shaderUniforms.current,
773
+ depthWrite: false,
774
+ depthTest: false,
775
+ transparent: true,
776
+ blending: THREE5.AdditiveBlending
777
+ });
778
+ const mesh = new THREE5.Mesh(geometry, material);
779
+ scene.add(mesh);
780
+ assetsRef.current = { mesh, geometry, material };
781
+ sizeRef.current = size;
782
+ updateMeshScale();
783
+ updateVisibility();
784
+ return () => {
785
+ scene.remove(mesh);
786
+ geometry.dispose();
787
+ material.dispose();
788
+ assetsRef.current = null;
789
+ };
790
+ },
791
+ [updateMeshScale, updateVisibility]
792
+ );
793
+ const handleRender = (0, import_react5.useCallback)(
794
+ (_context, _delta, elapsedTime) => {
795
+ shaderUniforms.current.uTime.value = elapsedTime;
796
+ },
797
+ []
798
+ );
799
+ const handleResize = (0, import_react5.useCallback)(
800
+ (_context, size) => {
801
+ sizeRef.current = size;
802
+ updateMeshScale();
803
+ },
804
+ [updateMeshScale]
805
+ );
806
+ const { containerRef } = useScene({
807
+ camera: { type: "orthographic", position: [0, 0, 10], near: 0.1, far: 100 },
808
+ onCreate: handleCreate,
809
+ onRender: handleRender,
810
+ onResize: handleResize
811
+ });
812
+ const showTimeline = (0, import_react5.useRef)(null);
813
+ const hideTimeline = (0, import_react5.useRef)(null);
814
+ const transitionTimeline = (0, import_react5.useRef)(null);
815
+ const timelineParams = (0, import_react5.useMemo)(
816
+ () => ({
817
+ tileAmplitude: sanitizedValues.tileAmplitude,
818
+ gradientAmplitude: sanitizedValues.gradientAmplitude,
819
+ gradientOffset: sanitizedValues.gradientOffset,
820
+ tileOffsetX: sanitizedValues.tileOffsetX,
821
+ duration: sanitizedValues.duration
822
+ }),
823
+ [
824
+ sanitizedValues.duration,
825
+ sanitizedValues.gradientAmplitude,
826
+ sanitizedValues.gradientOffset,
827
+ sanitizedValues.tileAmplitude,
828
+ sanitizedValues.tileOffsetX
829
+ ]
830
+ );
831
+ const primeShowAnimation = (0, import_react5.useCallback)(() => {
832
+ const uniforms = shaderUniforms.current;
833
+ uniforms.uTileProgressHorizontal.value = 1 + timelineParams.tileAmplitude * 0.5;
834
+ uniforms.uGradientProgress.value = 1 + timelineParams.gradientOffset;
835
+ uniforms.uBlueProgress.value = 1 + timelineParams.tileOffsetX;
836
+ uniforms.uSaturation.value = 1;
837
+ uniforms.uWhiteTileStrength.value = 0;
838
+ }, [
839
+ timelineParams.gradientOffset,
840
+ timelineParams.tileAmplitude,
841
+ timelineParams.tileOffsetX
842
+ ]);
843
+ const buildTimelines = (0, import_react5.useCallback)(() => {
844
+ const uniforms = shaderUniforms.current;
845
+ const glitchDuration = sanitizedRef.current.duration;
846
+ const desiredShowTotal = glitchDuration * (SHOW_BASE_DURATION / SHOW_PRIMARY_DURATION);
847
+ const desiredHideTotal = glitchDuration * (HIDE_BASE_DURATION / SHOW_PRIMARY_DURATION);
848
+ const showScale = desiredShowTotal / SHOW_BASE_DURATION;
849
+ const hideScale = desiredHideTotal / HIDE_BASE_DURATION;
850
+ const showAt = (time) => time / SHOW_BASE_DURATION * desiredShowTotal;
851
+ const hideAt = (time) => time / HIDE_BASE_DURATION * desiredHideTotal;
852
+ showTimeline.current?.kill();
853
+ hideTimeline.current?.kill();
854
+ transitionTimeline.current?.kill();
855
+ const showTl = import_gsap.gsap.timeline({ paused: true });
856
+ showTl.add(() => {
857
+ animationVisibleRef.current = true;
858
+ updateVisibility();
859
+ }, 0);
860
+ showTl.fromTo(
861
+ uniforms.uTileProgressHorizontal,
862
+ { value: 1 + timelineParams.tileAmplitude * 0.5 },
863
+ {
864
+ value: -timelineParams.tileAmplitude * 0.5,
865
+ duration: glitchDuration,
866
+ ease: "sine.inOut"
867
+ },
868
+ 0
869
+ );
870
+ showTl.fromTo(
871
+ uniforms.uGradientProgress,
872
+ { value: 1 + timelineParams.gradientOffset },
873
+ {
874
+ value: -timelineParams.gradientOffset * 0.5 - timelineParams.gradientAmplitude,
875
+ duration: 0.6 * showScale,
876
+ ease: "sine.inOut"
877
+ },
878
+ showAt(0.1)
879
+ );
880
+ showTl.fromTo(
881
+ uniforms.uBlueProgress,
882
+ { value: 1 + timelineParams.tileOffsetX },
883
+ {
884
+ value: -timelineParams.tileOffsetX * 0.5 - timelineParams.gradientAmplitude,
885
+ duration: 0.6 * showScale,
886
+ ease: "sine.inOut"
887
+ },
888
+ showAt(0.125)
889
+ );
890
+ showTl.fromTo(
891
+ uniforms.uSaturation,
892
+ { value: 1 },
893
+ { value: 1.01, duration: 0.52 * showScale, ease: "sine.inOut" },
894
+ 0
895
+ );
896
+ showTl.fromTo(
897
+ uniforms.uSaturation,
898
+ { value: 1 },
899
+ { value: 2.5, duration: 0.1 * showScale, ease: "sine.inOut" },
900
+ showAt(0.32)
901
+ );
902
+ showTl.fromTo(
903
+ uniforms.uSaturation,
904
+ { value: 2.5 },
905
+ { value: 1, duration: 1.8 * showScale, ease: "sine.out" },
906
+ showAt(0.92)
907
+ );
908
+ showTl.fromTo(
909
+ uniforms.uWhiteTileStrength,
910
+ { value: 0 },
911
+ { value: 0.1, duration: 1 * showScale, ease: "sine.inOut" },
912
+ showAt(0.3)
913
+ );
914
+ const hideTl = import_gsap.gsap.timeline({ paused: true });
915
+ hideTl.fromTo(
916
+ uniforms.uWhiteTileStrength,
917
+ { value: 0.1 },
918
+ { value: 0, duration: 1 * hideScale, ease: "sine.inOut" },
919
+ 0
920
+ );
921
+ hideTl.fromTo(
922
+ uniforms.uSaturation,
923
+ { value: 1 },
924
+ { value: 1.5, duration: 0.4 * hideScale, ease: "sine.inOut" },
925
+ hideAt(0.4)
926
+ );
927
+ hideTl.fromTo(
928
+ uniforms.uSaturation,
929
+ { value: 1.5 },
930
+ { value: 1, duration: 1 * hideScale, ease: "sine.inOut" },
931
+ hideAt(0.8)
932
+ );
933
+ hideTl.fromTo(
934
+ uniforms.uBlueProgress,
935
+ {
936
+ value: -timelineParams.tileOffsetX * 0.5 - timelineParams.gradientAmplitude
937
+ },
938
+ {
939
+ value: 1 + timelineParams.tileOffsetX,
940
+ duration: 0.6 * hideScale,
941
+ ease: "sine.inOut"
942
+ },
943
+ hideAt(0.2)
944
+ );
945
+ hideTl.fromTo(
946
+ uniforms.uTileProgressHorizontal,
947
+ { value: -timelineParams.tileAmplitude * 0.5 },
948
+ {
949
+ value: 1 + timelineParams.tileAmplitude * 0.5,
950
+ duration: 0.9 * hideScale,
951
+ ease: "sine.inOut"
952
+ },
953
+ hideAt(0.2)
954
+ );
955
+ hideTl.fromTo(
956
+ uniforms.uGradientProgress,
957
+ {
958
+ value: -timelineParams.gradientOffset * 0.5 - timelineParams.gradientAmplitude
959
+ },
960
+ {
961
+ value: 1 + timelineParams.gradientOffset,
962
+ duration: 0.6 * hideScale,
963
+ ease: "sine.inOut"
964
+ },
965
+ hideAt(0.225)
966
+ );
967
+ hideTl.add(() => {
968
+ animationVisibleRef.current = false;
969
+ updateVisibility();
970
+ onHideDone?.();
971
+ }, hideAt(HIDE_BASE_DURATION));
972
+ showTl.call(
973
+ () => {
974
+ onShowDone?.();
975
+ },
976
+ void 0,
977
+ showAt(0.65)
978
+ );
979
+ showTimeline.current = showTl;
980
+ hideTimeline.current = hideTl;
981
+ }, [onHideDone, onShowDone, timelineParams, updateVisibility]);
982
+ (0, import_react5.useEffect)(() => {
983
+ const uniforms = shaderUniforms.current;
984
+ uniforms.tDiffuse.value = diffuseTexture;
985
+ if (gradientTextureRef.current) {
986
+ uniforms.tGradient.value = gradientTextureRef.current;
987
+ }
988
+ uniforms.uTileAmplitude.value = sanitizedValues.tileAmplitude;
989
+ uniforms.uTileOffset.value.set(
990
+ sanitizedValues.tileOffsetX,
991
+ sanitizedValues.tileOffsetY
992
+ );
993
+ uniforms.uTileFrequency.value.set(
994
+ sanitizedValues.tileFrequencyX,
995
+ sanitizedValues.tileFrequencyY
996
+ );
997
+ uniforms.uWaveAmplitude.value = sanitizedValues.waveAmplitude;
998
+ uniforms.uWaveProgress.value = sanitizedValues.waveProgress;
999
+ uniforms.uWaveStrength.value.set(
1000
+ sanitizedValues.waveStrengthX,
1001
+ sanitizedValues.waveStrengthY
1002
+ );
1003
+ uniforms.uGradientProgress.value = sanitizedValues.gradientProgress;
1004
+ uniforms.uGradientOffset.value = sanitizedValues.gradientOffset;
1005
+ uniforms.uGradientAmplitude.value = sanitizedValues.gradientAmplitude;
1006
+ uniforms.uBlueProgress.value = sanitizedValues.blueProgress;
1007
+ uniforms.uBlueAmplitude.value = sanitizedValues.blueAmplitude;
1008
+ uniforms.uWhiteTileChances.value = sanitizedValues.whiteTileChances;
1009
+ uniforms.uWhiteTileFrequency.value = sanitizedValues.whiteTileFrequency;
1010
+ uniforms.uWhiteTileStrength.value = sanitizedValues.whiteTileStrength;
1011
+ uniforms.uSaturation.value = sanitizedValues.saturation;
1012
+ uniforms.uTileProgressVertical.value = BASE_TRANSITION_PROGRESS;
1013
+ updateMeshScale();
1014
+ updateVisibility();
1015
+ }, [diffuseTexture, sanitizedValues, updateMeshScale, updateVisibility]);
1016
+ (0, import_react5.useEffect)(() => {
1017
+ buildTimelines();
1018
+ return () => {
1019
+ showTimeline.current?.kill();
1020
+ hideTimeline.current?.kill();
1021
+ transitionTimeline.current?.kill();
1022
+ };
1023
+ }, [buildTimelines]);
1024
+ (0, import_react5.useEffect)(() => {
1025
+ const mesh = assetsRef.current?.mesh;
1026
+ if (mesh) {
1027
+ mesh.visible = sanitizedValues.debug || animationVisibleRef.current;
1028
+ }
1029
+ }, [sanitizedValues.debug]);
1030
+ const lastShowSignal = (0, import_react5.useRef)(settings.showSignal);
1031
+ (0, import_react5.useEffect)(() => {
1032
+ if (settings.showSignal === lastShowSignal.current) {
1033
+ return;
1034
+ }
1035
+ lastShowSignal.current = settings.showSignal;
1036
+ hideTimeline.current?.pause(0);
1037
+ primeShowAnimation();
1038
+ showTimeline.current?.seek(0).play();
1039
+ }, [primeShowAnimation, settings.showSignal]);
1040
+ const lastHideSignal = (0, import_react5.useRef)(settings.hideSignal);
1041
+ (0, import_react5.useEffect)(() => {
1042
+ if (settings.hideSignal === lastHideSignal.current) {
1043
+ return;
1044
+ }
1045
+ lastHideSignal.current = settings.hideSignal;
1046
+ showTimeline.current?.pause(0);
1047
+ hideTimeline.current?.seek(0).play();
1048
+ }, [settings.hideSignal]);
1049
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1050
+ "div",
1051
+ {
1052
+ ref: containerRef,
1053
+ className,
1054
+ style: {
1055
+ width: width ?? "100%",
1056
+ height: height ?? "100%",
1057
+ ...style
1058
+ },
1059
+ ...divProps
1060
+ }
1061
+ );
1062
+ }
1063
+ function createDiffuseTexture() {
1064
+ const size = 256;
1065
+ const data = new Uint8Array(size * size * 4);
1066
+ for (let y = 0; y < size; y += 1) {
1067
+ for (let x = 0; x < size; x += 1) {
1068
+ const i = (y * size + x) * 4;
1069
+ const stripe = Math.sin(y / size * Math.PI * 8) * 0.5 + 0.5;
1070
+ const tint = 0.25 + Math.sin(x / size * Math.PI * 2) * 0.15;
1071
+ const base = THREE5.MathUtils.clamp(stripe * 0.7 + tint, 0, 1);
1072
+ data[i] = base * 255;
1073
+ data[i + 1] = base * 200;
1074
+ data[i + 2] = base * 150;
1075
+ data[i + 3] = 255;
1076
+ }
1077
+ }
1078
+ const texture = new THREE5.DataTexture(data, size, size, THREE5.RGBAFormat);
1079
+ texture.needsUpdate = true;
1080
+ texture.colorSpace = THREE5.SRGBColorSpace;
1081
+ texture.wrapS = THREE5.RepeatWrapping;
1082
+ texture.wrapT = THREE5.RepeatWrapping;
1083
+ texture.magFilter = THREE5.LinearFilter;
1084
+ texture.minFilter = THREE5.LinearMipmapLinearFilter;
1085
+ texture.generateMipmaps = true;
1086
+ return texture;
1087
+ }
1088
+
1089
+ // src/components/EfectoAsciiEffect.tsx
1090
+ var import_react6 = require("react");
1091
+ var import_postprocessing = require("postprocessing");
1092
+ var THREE6 = __toESM(require("three"));
1093
+ var import_three = require("three");
1094
+
1095
+ // src/utils/efectoMediaAdjustments.ts
1096
+ var EFECTO_MEDIA_ADJUSTMENT_DEFAULTS = {
1097
+ brightness: 1,
1098
+ contrast: 1,
1099
+ saturation: 1
1100
+ };
1101
+ var resolveEfectoMediaAdjustments = (adjustments) => ({
1102
+ brightness: typeof adjustments?.brightness === "number" ? adjustments.brightness : EFECTO_MEDIA_ADJUSTMENT_DEFAULTS.brightness,
1103
+ contrast: typeof adjustments?.contrast === "number" ? adjustments.contrast : EFECTO_MEDIA_ADJUSTMENT_DEFAULTS.contrast,
1104
+ saturation: typeof adjustments?.saturation === "number" ? adjustments.saturation : EFECTO_MEDIA_ADJUSTMENT_DEFAULTS.saturation
1105
+ });
1106
+
1107
+ // src/shaders/efecto-ascii-effect/fragment.ts
1108
+ var fragment_default5 = `
1109
+ uniform float cellSize;
1110
+ uniform bool invert;
1111
+ uniform bool colorMode;
1112
+ uniform int asciiStyle;
1113
+
1114
+ // PostFX uniforms
1115
+ uniform float time;
1116
+ uniform vec2 resolution;
1117
+ uniform vec2 mousePos;
1118
+
1119
+ // Tier 1 uniforms
1120
+ uniform float scanlineIntensity;
1121
+ uniform float scanlineCount;
1122
+ uniform float targetFPS;
1123
+ uniform float jitterIntensity;
1124
+ uniform float jitterSpeed;
1125
+ uniform bool mouseGlowEnabled;
1126
+ uniform float mouseGlowRadius;
1127
+ uniform float mouseGlowIntensity;
1128
+ uniform float vignetteIntensity;
1129
+ uniform float vignetteRadius;
1130
+ uniform int colorPalette;
1131
+
1132
+ // Tier 2 uniforms
1133
+ uniform float curvature;
1134
+ uniform float aberrationStrength;
1135
+ uniform float noiseIntensity;
1136
+ uniform float noiseScale;
1137
+ uniform float noiseSpeed;
1138
+ uniform float waveAmplitude;
1139
+ uniform float waveFrequency;
1140
+ uniform float waveSpeed;
1141
+ uniform float glitchIntensity;
1142
+ uniform float glitchFrequency;
1143
+ uniform float brightnessAdjust;
1144
+ uniform float contrastAdjust;
1145
+
1146
+ // =======================
1147
+ // HELPER FUNCTIONS
1148
+ // =======================
1149
+
1150
+ // Pseudo-random function
1151
+ float random(vec2 st) {
1152
+ return fract(sin(dot(st, vec2(12.9898, 78.233))) * 43758.5453);
1153
+ }
1154
+
1155
+ // 2D Noise function
1156
+ float noise(vec2 st) {
1157
+ vec2 i = floor(st);
1158
+ vec2 f = fract(st);
1159
+ float a = random(i);
1160
+ float b = random(i + vec2(1.0, 0.0));
1161
+ float c = random(i + vec2(0.0, 1.0));
1162
+ float d = random(i + vec2(1.0, 1.0));
1163
+ vec2 u = f * f * (3.0 - 2.0 * f);
1164
+ return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
1165
+ }
1166
+
1167
+ // RGB to HSL conversion
1168
+ vec3 rgb2hsl(vec3 rgb) {
1169
+ float maxVal = max(max(rgb.r, rgb.g), rgb.b);
1170
+ float minVal = min(min(rgb.r, rgb.g), rgb.b);
1171
+ float delta = maxVal - minVal;
1172
+
1173
+ float h = 0.0;
1174
+ float s = 0.0;
1175
+ float l = (maxVal + minVal) * 0.5;
1176
+
1177
+ if (delta > 0.0001) {
1178
+ s = delta / (1.0 - abs(2.0 * l - 1.0));
1179
+
1180
+ if (maxVal == rgb.r) {
1181
+ h = mod((rgb.g - rgb.b) / delta, 6.0);
1182
+ } else if (maxVal == rgb.g) {
1183
+ h = (rgb.b - rgb.r) / delta + 2.0;
1184
+ } else {
1185
+ h = (rgb.r - rgb.g) / delta + 4.0;
1186
+ }
1187
+ h = h / 6.0;
1188
+ }
1189
+
1190
+ return vec3(h, s, l);
1191
+ }
1192
+
1193
+ // HSL to RGB conversion
1194
+ vec3 hsl2rgb(vec3 hsl) {
1195
+ float h = hsl.x;
1196
+ float s = hsl.y;
1197
+ float l = hsl.z;
1198
+
1199
+ float c = (1.0 - abs(2.0 * l - 1.0)) * s;
1200
+ float x = c * (1.0 - abs(mod(h * 6.0, 2.0) - 1.0));
1201
+ float m = l - c * 0.5;
1202
+
1203
+ vec3 rgb = vec3(0.0);
1204
+
1205
+ if (h < 1.0/6.0) {
1206
+ rgb = vec3(c, x, 0.0);
1207
+ } else if (h < 2.0/6.0) {
1208
+ rgb = vec3(x, c, 0.0);
1209
+ } else if (h < 3.0/6.0) {
1210
+ rgb = vec3(0.0, c, x);
1211
+ } else if (h < 4.0/6.0) {
1212
+ rgb = vec3(0.0, x, c);
1213
+ } else if (h < 5.0/6.0) {
1214
+ rgb = vec3(x, 0.0, c);
1215
+ } else {
1216
+ rgb = vec3(c, 0.0, x);
1217
+ }
1218
+
1219
+ return rgb + m;
1220
+ }
1221
+
1222
+ // Apply color palette
1223
+ vec3 applyColorPalette(vec3 color, float brightness, int palette) {
1224
+ if (palette == 0) return color; // Original
1225
+
1226
+ vec3 paletteColor = color;
1227
+
1228
+ if (palette == 1) { // Green phosphor
1229
+ paletteColor = vec3(0.0, brightness, 0.0) * 1.5;
1230
+ } else if (palette == 2) { // Amber
1231
+ paletteColor = vec3(brightness * 1.2, brightness * 0.7, 0.0);
1232
+ } else if (palette == 3) { // Cyan
1233
+ paletteColor = vec3(0.0, brightness * 0.9, brightness);
1234
+ } else if (palette == 4) { // Blue
1235
+ paletteColor = vec3(0.0, 0.0, brightness);
1236
+ }
1237
+
1238
+ return paletteColor;
1239
+ }
1240
+
1241
+ // Different character patterns based on style
1242
+ float getChar(float brightness, vec2 p, int style) {
1243
+ vec2 grid = floor(p * 4.0);
1244
+ float val = 0.0;
1245
+
1246
+ if (style == 0) {
1247
+ // Standard ASCII style
1248
+ if (brightness < 0.2) {
1249
+ val = (grid.x == 1.0 && grid.y == 1.0) ? 0.3 : 0.0;
1250
+ } else if (brightness < 0.35) {
1251
+ val = (grid.x == 1.0 || grid.x == 2.0) && (grid.y == 1.0 || grid.y == 2.0) ? 1.0 : 0.0;
1252
+ } else if (brightness < 0.5) {
1253
+ val = (grid.y == 1.0 || grid.y == 2.0) ? 1.0 : 0.0;
1254
+ } else if (brightness < 0.65) {
1255
+ val = (grid.y == 0.0 || grid.y == 3.0) ? 1.0 : (grid.y == 1.0 || grid.y == 2.0) ? 0.5 : 0.0;
1256
+ } else if (brightness < 0.8) {
1257
+ val = (grid.x == 0.0 || grid.x == 2.0 || grid.y == 0.0 || grid.y == 2.0) ? 1.0 : 0.3;
1258
+ } else {
1259
+ val = 1.0;
1260
+ }
1261
+ } else if (style == 1) {
1262
+ // Dense style
1263
+ if (brightness < 0.15) {
1264
+ val = 0.0;
1265
+ } else if (brightness < 0.3) {
1266
+ val = (grid.x >= 1.0 && grid.x <= 2.0 && grid.y >= 1.0 && grid.y <= 2.0) ? 0.6 : 0.0;
1267
+ } else if (brightness < 0.5) {
1268
+ val = (grid.y == 1.0 || grid.y == 2.0) ? 1.0 : 0.3;
1269
+ } else if (brightness < 0.7) {
1270
+ val = (grid.x == 0.0 || grid.x == 3.0 || grid.y == 0.0 || grid.y == 3.0) ? 1.0 : 0.6;
1271
+ } else {
1272
+ val = 1.0;
1273
+ }
1274
+ } else if (style == 2) {
1275
+ // Minimal style
1276
+ if (brightness < 0.25) {
1277
+ val = 0.0;
1278
+ } else if (brightness < 0.4) {
1279
+ val = (grid.x == 2.0 && grid.y == 2.0) ? 1.0 : 0.0;
1280
+ } else if (brightness < 0.6) {
1281
+ val = (grid.x == 1.0 || grid.x == 2.0) && grid.y == 2.0 ? 1.0 : 0.0;
1282
+ } else if (brightness < 0.8) {
1283
+ val = (grid.y == 1.0 || grid.y == 2.0) ? 1.0 : 0.0;
1284
+ } else {
1285
+ val = (grid.x <= 2.0 && grid.y <= 2.0) ? 1.0 : 0.3;
1286
+ }
1287
+ } else if (style == 3) {
1288
+ // Blocks style
1289
+ if (brightness < 0.2) {
1290
+ val = 0.0;
1291
+ } else if (brightness < 0.4) {
1292
+ val = (grid.x >= 1.0 && grid.x <= 2.0 && grid.y >= 1.0 && grid.y <= 2.0) ? 0.8 : 0.0;
1293
+ } else if (brightness < 0.6) {
1294
+ val = (grid.y <= 2.0) ? 0.9 : 0.0;
1295
+ } else if (brightness < 0.8) {
1296
+ val = (grid.x <= 2.0 || grid.y <= 2.0) ? 1.0 : 0.2;
1297
+ } else {
1298
+ val = 1.0;
1299
+ }
1300
+ }
1301
+
1302
+ return val;
1303
+ }
1304
+
1305
+ void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) {
1306
+ vec2 workingUV = uv;
1307
+
1308
+ // ===========================
1309
+ // TIER 2: PRE-PROCESSING
1310
+ // ===========================
1311
+
1312
+ // Screen curvature
1313
+ if (curvature > 0.0) {
1314
+ vec2 centered = workingUV * 2.0 - 1.0;
1315
+ float dist = dot(centered, centered);
1316
+ centered *= 1.0 + curvature * dist;
1317
+ workingUV = centered * 0.5 + 0.5;
1318
+
1319
+ // Make out-of-bounds transparent so parent background shows through
1320
+ if (workingUV.x < 0.0 || workingUV.x > 1.0 || workingUV.y < 0.0 || workingUV.y > 1.0) {
1321
+ outputColor = vec4(0.0, 0.0, 0.0, 0.0);
1322
+ return;
1323
+ }
1324
+ }
1325
+
1326
+ // Wave distortion
1327
+ if (waveAmplitude > 0.0) {
1328
+ workingUV.x += sin(workingUV.y * waveFrequency + time * waveSpeed) * waveAmplitude;
1329
+ workingUV.y += cos(workingUV.x * waveFrequency + time * waveSpeed) * waveAmplitude * 0.5;
1330
+ }
1331
+
1332
+ // ===========================
1333
+ // CORE ASCII RENDERING
1334
+ // ===========================
1335
+
1336
+ vec2 res = resolution;
1337
+ vec2 cellCount = res / cellSize;
1338
+ vec2 cellCoord = floor(workingUV * cellCount);
1339
+
1340
+ // Frame rate control
1341
+ if (targetFPS > 0.0) {
1342
+ float frameTime = 1.0 / targetFPS;
1343
+ float frameIndex = floor(time / frameTime);
1344
+ cellCoord = floor(workingUV * cellCount) + vec2(random(vec2(frameIndex)) * 0.5);
1345
+ }
1346
+
1347
+ vec2 cellUV = (cellCoord + 0.5) / cellCount;
1348
+
1349
+ // Chromatic aberration
1350
+ vec4 cellColor;
1351
+ if (aberrationStrength > 0.0) {
1352
+ float r = texture(inputBuffer, cellUV + vec2(aberrationStrength, 0.0)).r;
1353
+ float g = texture(inputBuffer, cellUV).g;
1354
+ float b = texture(inputBuffer, cellUV - vec2(aberrationStrength, 0.0)).b;
1355
+ cellColor = vec4(r, g, b, 1.0);
1356
+ } else {
1357
+ cellColor = texture(inputBuffer, cellUV);
1358
+ }
1359
+
1360
+ // Calculate brightness
1361
+ float brightness = dot(cellColor.rgb, vec3(0.299, 0.587, 0.114));
1362
+
1363
+ // Contrast and brightness adjustment
1364
+ brightness = (brightness - 0.5) * contrastAdjust + 0.5 + brightnessAdjust;
1365
+ brightness = clamp(brightness, 0.0, 1.0);
1366
+
1367
+ // Time-based noise
1368
+ if (noiseIntensity > 0.0) {
1369
+ float noiseVal = noise(workingUV * noiseScale * 100.0 + time * noiseSpeed);
1370
+ brightness = mix(brightness, noiseVal, noiseIntensity);
1371
+ }
1372
+
1373
+ // Jitter/fuzzy effect
1374
+ if (jitterIntensity > 0.0) {
1375
+ float jitter = random(cellCoord + floor(time * jitterSpeed) * 0.1) - 0.5;
1376
+ brightness += jitter * jitterIntensity;
1377
+ brightness = clamp(brightness, 0.0, 1.0);
1378
+ }
1379
+
1380
+ // RGB Glitch
1381
+ if (glitchIntensity > 0.0 && glitchFrequency > 0.0) {
1382
+ float glitchTrigger = random(vec2(time * glitchFrequency));
1383
+ if (glitchTrigger > 0.9) {
1384
+ float glitchOffset = (random(cellCoord + time) - 0.5) * glitchIntensity;
1385
+ cellColor.r = texture(inputBuffer, cellUV + vec2(glitchOffset, 0.0)).r;
1386
+ cellColor.b = texture(inputBuffer, cellUV - vec2(glitchOffset, 0.0)).b;
1387
+ }
1388
+ }
1389
+
1390
+ if (invert) {
1391
+ brightness = 1.0 - brightness;
1392
+ }
1393
+
1394
+ // Get local UV within the cell
1395
+ vec2 localUV = fract(workingUV * cellCount);
1396
+ float charValue = getChar(brightness, localUV, asciiStyle);
1397
+
1398
+ // ===========================
1399
+ // TIER 1: POST-PROCESSING
1400
+ // ===========================
1401
+
1402
+ vec3 finalColor;
1403
+
1404
+ if (colorMode) {
1405
+ finalColor = cellColor.rgb * charValue;
1406
+ } else {
1407
+ finalColor = vec3(brightness * charValue);
1408
+ }
1409
+
1410
+ // Color palette
1411
+ finalColor = applyColorPalette(finalColor, brightness, colorPalette);
1412
+
1413
+ // Mouse glow
1414
+ if (mouseGlowEnabled && mouseGlowRadius > 0.0) {
1415
+ vec2 pixelPos = workingUV * res;
1416
+ float dist = distance(pixelPos, mousePos);
1417
+ float glow = 1.0 - smoothstep(0.0, mouseGlowRadius, dist);
1418
+ glow = pow(glow, 2.0);
1419
+ finalColor *= 1.0 + glow * mouseGlowIntensity;
1420
+ }
1421
+
1422
+ // Scanlines
1423
+ if (scanlineIntensity > 0.0) {
1424
+ float scanline = sin(workingUV.y * scanlineCount * 3.14159) * 0.5 + 0.5;
1425
+ finalColor *= 1.0 - scanlineIntensity * (1.0 - scanline);
1426
+ }
1427
+
1428
+ // Vignette
1429
+ if (vignetteIntensity > 0.0) {
1430
+ vec2 centered = workingUV - 0.5;
1431
+ float dist = length(centered) / 0.707; // Normalize to corner distance
1432
+ float vignette = smoothstep(vignetteRadius, vignetteRadius - 0.5, dist);
1433
+ finalColor *= mix(1.0, vignette, vignetteIntensity);
1434
+ }
1435
+
1436
+ outputColor = vec4(finalColor, cellColor.a);
1437
+ }
1438
+ `;
1439
+
1440
+ // src/shaders/efecto-media-image/vertex.glsl
1441
+ var vertex_default5 = "uniform vec2 uvScale;\nuniform vec2 uvOffset;\nvarying vec2 vUv;\n\nvoid main() {\n // Apply texture scale and offset for cover-fill effect\n vUv = uv * uvScale + uvOffset;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}";
1442
+
1443
+ // src/shaders/efecto-media-image/fragment.glsl
1444
+ var fragment_default6 = "uniform sampler2D map;\nuniform float brightness;\nuniform float contrast;\nuniform float saturation;\nvarying vec2 vUv;\n\nvec3 adjustContrast(vec3 color, float contrastAmount) {\n return (color - 0.5) * contrastAmount + 0.5;\n}\n\nvec3 adjustSaturation(vec3 color, float saturationAmount) {\n float gray = dot(color, vec3(0.299, 0.587, 0.114));\n return mix(vec3(gray), color, saturationAmount);\n}\n\nvoid main() {\n vec4 texel = texture2D(map, vUv);\n vec3 color = texel.rgb;\n\n // Apply brightness\n color *= brightness;\n\n // Apply contrast\n color = adjustContrast(color, contrast);\n\n // Apply saturation\n color = adjustSaturation(color, saturation);\n\n gl_FragColor = vec4(color, texel.a);\n}";
1445
+
1446
+ // src/components/EfectoAsciiEffect.tsx
1447
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1448
+ var DEFAULT_RESOLUTION = new import_three.Vector2(1920, 1080);
1449
+ var DEFAULT_MOUSE_POSITION = new import_three.Vector2(0, 0);
1450
+ var PARALLAX_LERP = 0.12;
1451
+ var STYLE_MAP = {
1452
+ standard: 0,
1453
+ dense: 1,
1454
+ minimal: 2,
1455
+ blocks: 3
1456
+ };
1457
+ var COLOR_PALETTE_MAP = {
1458
+ original: 0,
1459
+ green: 1,
1460
+ amber: 2,
1461
+ cyan: 3,
1462
+ blue: 4
1463
+ };
1464
+ var EFECTO_ASCII_COMPONENT_DEFAULTS = {
1465
+ cellSize: 8,
1466
+ invert: false,
1467
+ colorMode: true,
1468
+ asciiStyle: "standard"
1469
+ };
1470
+ var EFECTO_ASCII_POST_PROCESSING_DEFAULTS = {
1471
+ scanlineIntensity: 0,
1472
+ scanlineCount: 200,
1473
+ targetFPS: 0,
1474
+ jitterIntensity: 0,
1475
+ jitterSpeed: 0,
1476
+ mouseGlowEnabled: false,
1477
+ mouseGlowRadius: 200,
1478
+ mouseGlowIntensity: 1.5,
1479
+ vignetteIntensity: 0,
1480
+ vignetteRadius: 0.8,
1481
+ colorPalette: COLOR_PALETTE_MAP.original,
1482
+ curvature: 0,
1483
+ aberrationStrength: 0,
1484
+ noiseIntensity: 0,
1485
+ noiseScale: 0.1,
1486
+ noiseSpeed: 0,
1487
+ waveAmplitude: 0,
1488
+ waveFrequency: 3,
1489
+ waveSpeed: 0.2,
1490
+ glitchIntensity: 0,
1491
+ glitchFrequency: 0,
1492
+ brightnessAdjust: 0,
1493
+ contrastAdjust: 1
1494
+ };
1495
+ var normalizePostProcessing = (overrides) => {
1496
+ const { colorPalette: overridePalette, ...otherOverrides } = overrides ?? {};
1497
+ const merged = {
1498
+ ...EFECTO_ASCII_POST_PROCESSING_DEFAULTS,
1499
+ ...otherOverrides
1500
+ };
1501
+ const paletteValue = overridePalette === void 0 ? merged.colorPalette : typeof overridePalette === "string" ? COLOR_PALETTE_MAP[overridePalette.toLowerCase()] ?? COLOR_PALETTE_MAP.original : overridePalette;
1502
+ return { ...merged, colorPalette: paletteValue };
1503
+ };
1504
+ var AsciiEffectImpl = class extends import_postprocessing.Effect {
1505
+ constructor(initialProps) {
1506
+ const uniformEntries = [
1507
+ ["cellSize", new import_three.Uniform(initialProps.cellSize)],
1508
+ ["invert", new import_three.Uniform(initialProps.invert)],
1509
+ ["colorMode", new import_three.Uniform(initialProps.colorMode)],
1510
+ ["asciiStyle", new import_three.Uniform(initialProps.asciiStyle)],
1511
+ ["time", new import_three.Uniform(0)],
1512
+ ["resolution", new import_three.Uniform(initialProps.resolution.clone())],
1513
+ ["mousePos", new import_three.Uniform(initialProps.mousePos.clone())],
1514
+ ["scanlineIntensity", new import_three.Uniform(initialProps.scanlineIntensity)],
1515
+ ["scanlineCount", new import_three.Uniform(initialProps.scanlineCount)],
1516
+ ["targetFPS", new import_three.Uniform(initialProps.targetFPS)],
1517
+ ["jitterIntensity", new import_three.Uniform(initialProps.jitterIntensity)],
1518
+ ["jitterSpeed", new import_three.Uniform(initialProps.jitterSpeed)],
1519
+ ["mouseGlowEnabled", new import_three.Uniform(initialProps.mouseGlowEnabled)],
1520
+ ["mouseGlowRadius", new import_three.Uniform(initialProps.mouseGlowRadius)],
1521
+ ["mouseGlowIntensity", new import_three.Uniform(initialProps.mouseGlowIntensity)],
1522
+ ["vignetteIntensity", new import_three.Uniform(initialProps.vignetteIntensity)],
1523
+ ["vignetteRadius", new import_three.Uniform(initialProps.vignetteRadius)],
1524
+ ["colorPalette", new import_three.Uniform(initialProps.colorPalette)],
1525
+ ["curvature", new import_three.Uniform(initialProps.curvature)],
1526
+ ["aberrationStrength", new import_three.Uniform(initialProps.aberrationStrength)],
1527
+ ["noiseIntensity", new import_three.Uniform(initialProps.noiseIntensity)],
1528
+ ["noiseScale", new import_three.Uniform(initialProps.noiseScale)],
1529
+ ["noiseSpeed", new import_three.Uniform(initialProps.noiseSpeed)],
1530
+ ["waveAmplitude", new import_three.Uniform(initialProps.waveAmplitude)],
1531
+ ["waveFrequency", new import_three.Uniform(initialProps.waveFrequency)],
1532
+ ["waveSpeed", new import_three.Uniform(initialProps.waveSpeed)],
1533
+ ["glitchIntensity", new import_three.Uniform(initialProps.glitchIntensity)],
1534
+ ["glitchFrequency", new import_three.Uniform(initialProps.glitchFrequency)],
1535
+ ["brightnessAdjust", new import_three.Uniform(initialProps.brightnessAdjust)],
1536
+ ["contrastAdjust", new import_three.Uniform(initialProps.contrastAdjust)]
1537
+ ];
1538
+ super("AsciiEffect", fragment_default5, {
1539
+ blendFunction: import_postprocessing.BlendFunction.SRC,
1540
+ uniforms: new Map(uniformEntries)
1541
+ });
1542
+ this.time = 0;
1543
+ this.deltaAccumulator = 0;
1544
+ }
1545
+ updateUniforms(nextProps) {
1546
+ if (nextProps.cellSize !== void 0) {
1547
+ this.uniforms.get("cellSize").value = nextProps.cellSize;
1548
+ }
1549
+ if (nextProps.invert !== void 0) {
1550
+ this.uniforms.get("invert").value = nextProps.invert;
1551
+ }
1552
+ if (nextProps.colorMode !== void 0) {
1553
+ this.uniforms.get("colorMode").value = nextProps.colorMode;
1554
+ }
1555
+ if (nextProps.asciiStyle !== void 0) {
1556
+ this.uniforms.get("asciiStyle").value = nextProps.asciiStyle;
1557
+ }
1558
+ if (nextProps.resolution) {
1559
+ this.setVector2Uniform("resolution", nextProps.resolution);
1560
+ }
1561
+ if (nextProps.mousePos) {
1562
+ this.setVector2Uniform("mousePos", nextProps.mousePos);
1563
+ }
1564
+ const uniformKeys = Object.keys(
1565
+ EFECTO_ASCII_POST_PROCESSING_DEFAULTS
1566
+ );
1567
+ for (const key of uniformKeys) {
1568
+ if (nextProps[key] !== void 0) {
1569
+ this.uniforms.get(key).value = nextProps[key];
1570
+ }
1571
+ }
1572
+ }
1573
+ setVector2Uniform(key, nextValue) {
1574
+ const uniform = this.uniforms.get(key);
1575
+ if (!uniform) {
1576
+ return;
1577
+ }
1578
+ if (uniform.value instanceof import_three.Vector2) {
1579
+ uniform.value.copy(nextValue);
1580
+ return;
1581
+ }
1582
+ uniform.value = nextValue.clone();
1583
+ }
1584
+ update(_renderer, _inputBuffer, deltaTime) {
1585
+ const targetFPS = this.uniforms.get("targetFPS").value;
1586
+ if (targetFPS > 0) {
1587
+ const frameDuration = 1 / targetFPS;
1588
+ this.deltaAccumulator += deltaTime;
1589
+ if (this.deltaAccumulator >= frameDuration) {
1590
+ this.time += frameDuration;
1591
+ this.deltaAccumulator = this.deltaAccumulator % frameDuration;
1592
+ }
1593
+ } else {
1594
+ this.time += deltaTime;
1595
+ }
1596
+ this.uniforms.get("time").value = this.time;
1597
+ }
1598
+ };
1599
+ function AsciiEffect({
1600
+ cellSize = EFECTO_ASCII_COMPONENT_DEFAULTS.cellSize,
1601
+ invert = EFECTO_ASCII_COMPONENT_DEFAULTS.invert,
1602
+ colorMode = EFECTO_ASCII_COMPONENT_DEFAULTS.colorMode,
1603
+ asciiStyle: asciiStyleProp = EFECTO_ASCII_COMPONENT_DEFAULTS.asciiStyle,
1604
+ imageUrl,
1605
+ resolution,
1606
+ mousePosition,
1607
+ postProcessing = {},
1608
+ mediaAdjustments,
1609
+ mouseParallax = false,
1610
+ parallaxIntensity = 0.5,
1611
+ className,
1612
+ style,
1613
+ width,
1614
+ height,
1615
+ ...divProps
1616
+ }) {
1617
+ const composerRef = (0, import_react6.useRef)(null);
1618
+ const effectRef = (0, import_react6.useRef)(null);
1619
+ const assetsRef = (0, import_react6.useRef)(null);
1620
+ const loadedTextureRef = (0, import_react6.useRef)(null);
1621
+ const cameraRef = (0, import_react6.useRef)(null);
1622
+ const mediaUniformsRef = (0, import_react6.useRef)({
1623
+ map: { value: null },
1624
+ brightness: { value: EFECTO_MEDIA_ADJUSTMENT_DEFAULTS.brightness },
1625
+ contrast: { value: EFECTO_MEDIA_ADJUSTMENT_DEFAULTS.contrast },
1626
+ saturation: { value: EFECTO_MEDIA_ADJUSTMENT_DEFAULTS.saturation },
1627
+ uvScale: { value: new import_three.Vector2(1, 1) },
1628
+ uvOffset: { value: new import_three.Vector2(0, 0) }
1629
+ });
1630
+ const pointerBoundsRef = (0, import_react6.useRef)({
1631
+ left: 0,
1632
+ top: 0,
1633
+ width: 1,
1634
+ height: 1
1635
+ });
1636
+ const manualResolutionRef = (0, import_react6.useRef)(
1637
+ resolution ? resolution.clone() : null
1638
+ );
1639
+ manualResolutionRef.current = resolution ? resolution.clone() : null;
1640
+ const resolutionRef = (0, import_react6.useRef)(
1641
+ manualResolutionRef.current ? manualResolutionRef.current.clone() : DEFAULT_RESOLUTION.clone()
1642
+ );
1643
+ const autoMouseRef = (0, import_react6.useRef)(DEFAULT_MOUSE_POSITION.clone());
1644
+ const usesExternalMouseRef = (0, import_react6.useRef)(Boolean(mousePosition));
1645
+ usesExternalMouseRef.current = Boolean(mousePosition);
1646
+ const viewportSizeRef = (0, import_react6.useRef)({ width: 1, height: 1 });
1647
+ const meshDisplaySizeRef = (0, import_react6.useRef)({ width: 1, height: 1 });
1648
+ const parallaxStateRef = (0, import_react6.useRef)({
1649
+ targetX: 0,
1650
+ targetY: 0,
1651
+ rotationX: 0,
1652
+ rotationY: 0
1653
+ });
1654
+ const parallaxIntensityRef = (0, import_react6.useRef)(
1655
+ THREE6.MathUtils.clamp(parallaxIntensity ?? 0, 0, 1)
1656
+ );
1657
+ parallaxIntensityRef.current = THREE6.MathUtils.clamp(
1658
+ parallaxIntensity ?? 0,
1659
+ 0,
1660
+ 1
1661
+ );
1662
+ const normalizedStyle = typeof asciiStyleProp === "string" ? asciiStyleProp.toLowerCase() ?? EFECTO_ASCII_COMPONENT_DEFAULTS.asciiStyle : EFECTO_ASCII_COMPONENT_DEFAULTS.asciiStyle;
1663
+ const asciiStyle = STYLE_MAP[normalizedStyle] ?? STYLE_MAP.standard;
1664
+ const resolvedPostProcessing = (0, import_react6.useMemo)(
1665
+ () => normalizePostProcessing(postProcessing),
1666
+ [postProcessing]
1667
+ );
1668
+ const resolvedMediaAdjustments = (0, import_react6.useMemo)(
1669
+ () => resolveEfectoMediaAdjustments(mediaAdjustments),
1670
+ [mediaAdjustments]
1671
+ );
1672
+ const initialEffectPropsRef = (0, import_react6.useRef)(null);
1673
+ if (!initialEffectPropsRef.current) {
1674
+ initialEffectPropsRef.current = {
1675
+ cellSize,
1676
+ invert,
1677
+ colorMode,
1678
+ asciiStyle,
1679
+ resolution: resolutionRef.current.clone(),
1680
+ mousePos: (mousePosition ?? DEFAULT_MOUSE_POSITION).clone(),
1681
+ ...resolvedPostProcessing
1682
+ };
1683
+ }
1684
+ const updateTextureUVTransform = (0, import_react6.useCallback)(() => {
1685
+ const texture = loadedTextureRef.current;
1686
+ if (!texture || !texture.image) {
1687
+ return;
1688
+ }
1689
+ const { width: planeWidth, height: planeHeight } = meshDisplaySizeRef.current;
1690
+ if (planeWidth <= 0 || planeHeight <= 0) {
1691
+ return;
1692
+ }
1693
+ const image = texture.image;
1694
+ if (!image.width || !image.height) {
1695
+ return;
1696
+ }
1697
+ const imageAspect = image.width / image.height;
1698
+ const planeAspect = planeWidth / planeHeight;
1699
+ let scaleX = 1;
1700
+ let scaleY = 1;
1701
+ if (imageAspect > planeAspect) {
1702
+ scaleX = imageAspect / planeAspect;
1703
+ } else {
1704
+ scaleY = planeAspect / imageAspect;
1705
+ }
1706
+ const uvScaleX = 1 / scaleX;
1707
+ const uvScaleY = 1 / scaleY;
1708
+ const offsetX = (1 - uvScaleX) * 0.5;
1709
+ const offsetY = (1 - uvScaleY) * 0.5;
1710
+ mediaUniformsRef.current.uvScale.value.set(uvScaleX, uvScaleY);
1711
+ mediaUniformsRef.current.uvOffset.value.set(offsetX, offsetY);
1712
+ const assets = assetsRef.current;
1713
+ if (assets) {
1714
+ assets.material.uniforms.uvScale.value.set(
1715
+ uvScaleX,
1716
+ uvScaleY
1717
+ );
1718
+ assets.material.uniforms.uvOffset.value.set(
1719
+ offsetX,
1720
+ offsetY
1721
+ );
1722
+ }
1723
+ }, []);
1724
+ const updateMeshScale = (0, import_react6.useCallback)(() => {
1725
+ const assets = assetsRef.current;
1726
+ if (!assets) return;
1727
+ const { width: width2, height: height2 } = viewportSizeRef.current;
1728
+ const camera = cameraRef.current;
1729
+ let viewWidth = width2;
1730
+ let viewHeight = height2;
1731
+ if (camera instanceof THREE6.PerspectiveCamera) {
1732
+ const distance = Math.abs(camera.position.z - assets.mesh.position.z);
1733
+ const verticalFov = THREE6.MathUtils.degToRad(camera.fov);
1734
+ viewHeight = 2 * Math.tan(verticalFov / 2) * distance;
1735
+ viewWidth = viewHeight * camera.aspect;
1736
+ }
1737
+ const texture = loadedTextureRef.current;
1738
+ if (!texture || !texture.image) {
1739
+ assets.mesh.scale.set(viewWidth, viewHeight, 1);
1740
+ meshDisplaySizeRef.current = { width: viewWidth, height: viewHeight };
1741
+ return;
1742
+ }
1743
+ const image = texture.image;
1744
+ const imageAspect = image.width && image.height ? image.width / Math.max(1, image.height) : 1;
1745
+ const viewportAspect = viewWidth / Math.max(1, viewHeight);
1746
+ let meshWidth = viewWidth;
1747
+ let meshHeight = viewHeight;
1748
+ if (imageAspect > viewportAspect) {
1749
+ meshHeight = viewHeight;
1750
+ meshWidth = viewHeight * imageAspect;
1751
+ } else {
1752
+ meshWidth = viewWidth;
1753
+ meshHeight = viewWidth / Math.max(1e-4, imageAspect);
1754
+ }
1755
+ assets.mesh.scale.set(meshWidth, meshHeight, 1);
1756
+ meshDisplaySizeRef.current = { width: meshWidth, height: meshHeight };
1757
+ updateTextureUVTransform();
1758
+ }, [updateTextureUVTransform]);
1759
+ const updatePointerBounds = (0, import_react6.useCallback)(
1760
+ (container = containerRef.current) => {
1761
+ if (!container) return;
1762
+ const bounds = container.getBoundingClientRect();
1763
+ pointerBoundsRef.current = {
1764
+ left: bounds.left,
1765
+ top: bounds.top,
1766
+ width: bounds.width || 1,
1767
+ height: bounds.height || 1
1768
+ };
1769
+ },
1770
+ []
1771
+ );
1772
+ const resetParallax = (0, import_react6.useCallback)(() => {
1773
+ parallaxStateRef.current.targetX = 0;
1774
+ parallaxStateRef.current.targetY = 0;
1775
+ parallaxStateRef.current.rotationX = 0;
1776
+ parallaxStateRef.current.rotationY = 0;
1777
+ const mesh = assetsRef.current?.mesh;
1778
+ if (mesh) {
1779
+ mesh.rotation.x = 0;
1780
+ mesh.rotation.y = 0;
1781
+ }
1782
+ }, []);
1783
+ const handleCreate = (0, import_react6.useCallback)(
1784
+ ({ scene, camera, renderer, size }) => {
1785
+ renderer.outputColorSpace = THREE6.SRGBColorSpace;
1786
+ renderer.toneMapping = THREE6.NoToneMapping;
1787
+ if (!manualResolutionRef.current) {
1788
+ resolutionRef.current.set(size.width, size.height);
1789
+ }
1790
+ viewportSizeRef.current = { width: size.width, height: size.height };
1791
+ const geometry = new THREE6.PlaneGeometry(1, 1);
1792
+ const material = new THREE6.ShaderMaterial({
1793
+ vertexShader: vertex_default5,
1794
+ fragmentShader: fragment_default6,
1795
+ uniforms: mediaUniformsRef.current,
1796
+ transparent: true,
1797
+ depthTest: false
1798
+ });
1799
+ material.toneMapped = false;
1800
+ const mesh = new THREE6.Mesh(geometry, material);
1801
+ scene.add(mesh);
1802
+ if (loadedTextureRef.current) {
1803
+ mediaUniformsRef.current.map.value = loadedTextureRef.current;
1804
+ material.uniforms.map.value = loadedTextureRef.current;
1805
+ }
1806
+ assetsRef.current = { mesh, geometry, material };
1807
+ cameraRef.current = camera;
1808
+ updateMeshScale();
1809
+ const composer = new import_postprocessing.EffectComposer(renderer);
1810
+ const renderPass = new import_postprocessing.RenderPass(scene, camera);
1811
+ const effect = new AsciiEffectImpl({
1812
+ ...initialEffectPropsRef.current,
1813
+ resolution: resolutionRef.current.clone()
1814
+ });
1815
+ const effectPass = new import_postprocessing.EffectPass(camera, effect);
1816
+ effectPass.renderToScreen = true;
1817
+ composer.addPass(renderPass);
1818
+ composer.addPass(effectPass);
1819
+ composer.setSize(size.width, size.height);
1820
+ composerRef.current = composer;
1821
+ effectRef.current = effect;
1822
+ return () => {
1823
+ composer.dispose();
1824
+ if (assetsRef.current) {
1825
+ scene.remove(assetsRef.current.mesh);
1826
+ assetsRef.current.geometry.dispose();
1827
+ assetsRef.current.material.dispose();
1828
+ assetsRef.current = null;
1829
+ }
1830
+ cameraRef.current = null;
1831
+ loadedTextureRef.current?.dispose();
1832
+ loadedTextureRef.current = null;
1833
+ composerRef.current = null;
1834
+ effectRef.current = null;
1835
+ };
1836
+ },
1837
+ [updateMeshScale]
1838
+ );
1839
+ const handleRender = (0, import_react6.useCallback)(
1840
+ (_context, delta, _elapsed) => {
1841
+ const assets = assetsRef.current;
1842
+ if (assets) {
1843
+ const enabled = mouseParallax && parallaxIntensityRef.current > 0 && assets.mesh;
1844
+ if (enabled) {
1845
+ parallaxStateRef.current.rotationX += (parallaxStateRef.current.targetX - parallaxStateRef.current.rotationX) * PARALLAX_LERP;
1846
+ parallaxStateRef.current.rotationY += (parallaxStateRef.current.targetY - parallaxStateRef.current.rotationY) * PARALLAX_LERP;
1847
+ assets.mesh.rotation.x = parallaxStateRef.current.rotationX;
1848
+ assets.mesh.rotation.y = parallaxStateRef.current.rotationY;
1849
+ } else if (Math.abs(assets.mesh.rotation.x) > 1e-3 || Math.abs(assets.mesh.rotation.y) > 1e-3) {
1850
+ assets.mesh.rotation.x *= 0.9;
1851
+ assets.mesh.rotation.y *= 0.9;
1852
+ }
1853
+ }
1854
+ composerRef.current?.render(delta);
1855
+ },
1856
+ [mouseParallax]
1857
+ );
1858
+ const handleResize = (0, import_react6.useCallback)(
1859
+ (_context, size) => {
1860
+ const composer = composerRef.current;
1861
+ composer?.setSize(size.width, size.height);
1862
+ viewportSizeRef.current = { width: size.width, height: size.height };
1863
+ updateMeshScale();
1864
+ updatePointerBounds(containerRef.current);
1865
+ if (manualResolutionRef.current) {
1866
+ resolutionRef.current.copy(manualResolutionRef.current);
1867
+ } else {
1868
+ resolutionRef.current.set(size.width, size.height);
1869
+ }
1870
+ effectRef.current?.updateUniforms({
1871
+ resolution: resolutionRef.current
1872
+ });
1873
+ if (!usesExternalMouseRef.current) {
1874
+ autoMouseRef.current.set(size.width / 2, size.height / 2);
1875
+ effectRef.current?.updateUniforms({ mousePos: autoMouseRef.current });
1876
+ }
1877
+ },
1878
+ [updateMeshScale, updatePointerBounds]
1879
+ );
1880
+ const { containerRef, contextRef } = useScene({
1881
+ camera: {
1882
+ type: "perspective",
1883
+ near: 0.1,
1884
+ far: 100,
1885
+ position: [0, 0, 10],
1886
+ fov: 50
1887
+ },
1888
+ onCreate: handleCreate,
1889
+ onRender: handleRender,
1890
+ onResize: handleResize,
1891
+ manualRender: true
1892
+ });
1893
+ (0, import_react6.useEffect)(() => {
1894
+ if (!mouseParallax) {
1895
+ resetParallax();
1896
+ }
1897
+ }, [mouseParallax, resetParallax]);
1898
+ (0, import_react6.useEffect)(() => {
1899
+ if (!imageUrl) return;
1900
+ let disposed = false;
1901
+ const loader = new THREE6.TextureLoader();
1902
+ loader.load(
1903
+ imageUrl,
1904
+ (texture) => {
1905
+ if (disposed) {
1906
+ texture.dispose();
1907
+ return;
1908
+ }
1909
+ texture.colorSpace = THREE6.SRGBColorSpace;
1910
+ texture.wrapS = THREE6.ClampToEdgeWrapping;
1911
+ texture.wrapT = THREE6.ClampToEdgeWrapping;
1912
+ texture.minFilter = THREE6.LinearFilter;
1913
+ texture.magFilter = THREE6.LinearFilter;
1914
+ loadedTextureRef.current?.dispose();
1915
+ loadedTextureRef.current = texture;
1916
+ mediaUniformsRef.current.map.value = texture;
1917
+ const assets = assetsRef.current;
1918
+ if (assets) {
1919
+ assets.material.uniforms.map.value = texture;
1920
+ }
1921
+ updateMeshScale();
1922
+ updateTextureUVTransform();
1923
+ },
1924
+ void 0,
1925
+ () => {
1926
+ if (disposed) return;
1927
+ loadedTextureRef.current?.dispose();
1928
+ loadedTextureRef.current = null;
1929
+ }
1930
+ );
1931
+ return () => {
1932
+ disposed = true;
1933
+ };
1934
+ }, [imageUrl, updateMeshScale, updateTextureUVTransform]);
1935
+ (0, import_react6.useEffect)(() => {
1936
+ const effect = effectRef.current;
1937
+ if (!effect) return;
1938
+ effect.updateUniforms({
1939
+ cellSize,
1940
+ invert,
1941
+ colorMode,
1942
+ asciiStyle
1943
+ });
1944
+ }, [asciiStyle, cellSize, colorMode, invert]);
1945
+ (0, import_react6.useEffect)(() => {
1946
+ if (!effectRef.current) return;
1947
+ effectRef.current.updateUniforms(resolvedPostProcessing);
1948
+ }, [resolvedPostProcessing]);
1949
+ (0, import_react6.useEffect)(() => {
1950
+ mediaUniformsRef.current.brightness.value = resolvedMediaAdjustments.brightness;
1951
+ mediaUniformsRef.current.contrast.value = resolvedMediaAdjustments.contrast;
1952
+ mediaUniformsRef.current.saturation.value = resolvedMediaAdjustments.saturation;
1953
+ const material = assetsRef.current?.material;
1954
+ if (material) {
1955
+ material.uniforms.brightness.value = resolvedMediaAdjustments.brightness;
1956
+ material.uniforms.contrast.value = resolvedMediaAdjustments.contrast;
1957
+ material.uniforms.saturation.value = resolvedMediaAdjustments.saturation;
1958
+ }
1959
+ }, [resolvedMediaAdjustments]);
1960
+ (0, import_react6.useEffect)(() => {
1961
+ if (!mousePosition || !effectRef.current) return;
1962
+ effectRef.current.updateUniforms({ mousePos: mousePosition });
1963
+ }, [mousePosition]);
1964
+ (0, import_react6.useEffect)(() => {
1965
+ if (!resolution || !effectRef.current) return;
1966
+ resolutionRef.current.copy(resolution);
1967
+ manualResolutionRef.current = resolution.clone();
1968
+ effectRef.current.updateUniforms({ resolution });
1969
+ }, [resolution]);
1970
+ (0, import_react6.useEffect)(() => {
1971
+ if (mousePosition) return;
1972
+ const container = containerRef.current;
1973
+ if (!container) return;
1974
+ updatePointerBounds(container);
1975
+ const updateFromEvent = (event) => {
1976
+ const bounds = pointerBoundsRef.current;
1977
+ autoMouseRef.current.set(
1978
+ event.clientX - bounds.left,
1979
+ bounds.height - (event.clientY - bounds.top)
1980
+ );
1981
+ effectRef.current?.updateUniforms({ mousePos: autoMouseRef.current });
1982
+ if (mouseParallax && bounds.width > 0 && bounds.height > 0) {
1983
+ const normalizedX = ((event.clientX - bounds.left) / bounds.width - 0.5) * 2;
1984
+ const normalizedY = ((event.clientY - bounds.top) / bounds.height - 0.5) * 2;
1985
+ const intensity = parallaxIntensityRef.current;
1986
+ parallaxStateRef.current.targetY = 0.3 * normalizedX * intensity;
1987
+ parallaxStateRef.current.targetX = -0.2 * normalizedY * intensity;
1988
+ }
1989
+ };
1990
+ const handlePointerEnter = () => {
1991
+ updatePointerBounds();
1992
+ };
1993
+ const resetToCenter = () => {
1994
+ const size = contextRef.current?.size ?? {
1995
+ width: container.clientWidth || 1,
1996
+ height: container.clientHeight || 1
1997
+ };
1998
+ autoMouseRef.current.set(size.width / 2, size.height / 2);
1999
+ effectRef.current?.updateUniforms({ mousePos: autoMouseRef.current });
2000
+ if (mouseParallax) {
2001
+ resetParallax();
2002
+ }
2003
+ };
2004
+ resetToCenter();
2005
+ container.addEventListener("pointermove", updateFromEvent);
2006
+ container.addEventListener("pointerenter", handlePointerEnter);
2007
+ container.addEventListener("pointerleave", resetToCenter);
2008
+ return () => {
2009
+ container.removeEventListener("pointermove", updateFromEvent);
2010
+ container.removeEventListener("pointerenter", handlePointerEnter);
2011
+ container.removeEventListener("pointerleave", resetToCenter);
2012
+ };
2013
+ }, [
2014
+ containerRef,
2015
+ contextRef,
2016
+ mouseParallax,
2017
+ mousePosition,
2018
+ resetParallax,
2019
+ updatePointerBounds
2020
+ ]);
2021
+ (0, import_react6.useEffect)(() => {
2022
+ parallaxIntensityRef.current = THREE6.MathUtils.clamp(
2023
+ parallaxIntensity ?? 0,
2024
+ 0,
2025
+ 1
2026
+ );
2027
+ }, [parallaxIntensity]);
2028
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2029
+ "div",
2030
+ {
2031
+ ref: containerRef,
2032
+ className,
2033
+ style: {
2034
+ width: width ?? "100%",
2035
+ height: height ?? "100%",
2036
+ ...style
2037
+ },
2038
+ ...divProps
2039
+ }
2040
+ );
2041
+ }
2042
+
2043
+ // src/components/EfectoAsciiWrapper.tsx
2044
+ var import_jsx_runtime6 = require("react/jsx-runtime");
2045
+ function EfectoAsciiWrapper({
2046
+ settings,
2047
+ imageUrl,
2048
+ mediaAdjustments,
2049
+ mouseParallax,
2050
+ parallaxIntensity,
2051
+ ...passThrough
2052
+ }) {
2053
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2054
+ AsciiEffect,
2055
+ {
2056
+ asciiStyle: settings.asciiStyle,
2057
+ cellSize: settings.cellSize,
2058
+ invert: settings.invert,
2059
+ colorMode: settings.colorMode,
2060
+ postProcessing: settings.postProcessing,
2061
+ imageUrl,
2062
+ mediaAdjustments,
2063
+ mouseParallax,
2064
+ parallaxIntensity,
2065
+ ...passThrough
2066
+ }
2067
+ );
2068
+ }
2069
+
2070
+ // src/components/Efecto.tsx
2071
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2072
+ var ASCII_BASE_DEFAULTS = {
2073
+ cellSize: EFECTO_ASCII_COMPONENT_DEFAULTS.cellSize,
2074
+ invert: EFECTO_ASCII_COMPONENT_DEFAULTS.invert,
2075
+ colorMode: EFECTO_ASCII_COMPONENT_DEFAULTS.colorMode,
2076
+ asciiStyle: EFECTO_ASCII_COMPONENT_DEFAULTS.asciiStyle
2077
+ };
2078
+ function Efecto({
2079
+ postProcessing,
2080
+ src,
2081
+ mouseParallax = false,
2082
+ parallaxIntensity = 0.5,
2083
+ mediaAdjustments,
2084
+ cellSize,
2085
+ invert,
2086
+ colorMode,
2087
+ style,
2088
+ asciiStyle,
2089
+ ...wrapperProps
2090
+ }) {
2091
+ const resolvedStyle = style ?? asciiStyle ?? ASCII_BASE_DEFAULTS.asciiStyle;
2092
+ const baseAsciiProps = {
2093
+ cellSize: cellSize ?? ASCII_BASE_DEFAULTS.cellSize,
2094
+ invert: invert ?? ASCII_BASE_DEFAULTS.invert,
2095
+ colorMode: colorMode ?? ASCII_BASE_DEFAULTS.colorMode,
2096
+ asciiStyle: resolvedStyle
2097
+ };
2098
+ const asciiSettings = {
2099
+ ...baseAsciiProps,
2100
+ postProcessing
2101
+ };
2102
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2103
+ EfectoAsciiWrapper,
2104
+ {
2105
+ settings: asciiSettings,
2106
+ imageUrl: src,
2107
+ mediaAdjustments,
2108
+ mouseParallax,
2109
+ parallaxIntensity,
2110
+ ...wrapperProps
2111
+ }
2112
+ );
2113
+ }
561
2114
  // Annotate the CommonJS export names for ESM import in node:
562
2115
  0 && (module.exports = {
2116
+ EFECTO_ASCII_COMPONENT_DEFAULTS,
2117
+ EFECTO_ASCII_POST_PROCESSING_DEFAULTS,
2118
+ Efecto,
563
2119
  FractalFlower,
2120
+ MenuGlitch,
564
2121
  OranoParticles,
565
2122
  ShaderArt
566
2123
  });