@toriistudio/shader-ui 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -278,7 +278,7 @@ type NoiseWarpPassUniforms = {
278
278
  radius: number;
279
279
  };
280
280
 
281
- type CombineMode = "add" | "screen" | "multiply" | "overlay" | "max" | "min" | "difference" | "alphaOver" | "premultipliedOver" | "lerp" | "mask";
281
+ type CombineShaderMode = "add" | "screen" | "multiply" | "overlay" | "max" | "min" | "difference" | "alphaOver" | "premultipliedOver" | "lerp" | "mask";
282
282
 
283
283
  type DitherPulseRingProps = {
284
284
  spriteTextureSrc?: string;
@@ -286,7 +286,7 @@ type DitherPulseRingProps = {
286
286
  diffuseEnabled?: boolean;
287
287
  blurEnabled?: boolean;
288
288
  noiseWarpEnabled?: boolean;
289
- combineMode?: CombineMode;
289
+ combineMode?: CombineShaderMode;
290
290
  noiseWarpRadius?: NoiseWarpPassUniforms["radius"];
291
291
  noiseWarpStrength?: NoiseWarpPassUniforms["strength"];
292
292
  diffuseRadius?: DiffusePassUniforms["diffuseRadius"];
@@ -308,4 +308,24 @@ type DitherPulseRingProps = {
308
308
  };
309
309
  declare function DitherPulseRing({ spriteTextureSrc, glyphDitherEnabled, diffuseEnabled, blurEnabled, noiseWarpEnabled, combineMode, noiseWarpRadius, noiseWarpStrength, diffuseRadius, blurRadius, borderThickness, borderIntensity, borderColor, borderDitherStrength, borderTonemap, borderAlpha, ringColor, ringSpeed, ringPosition, ringAlpha, width, height, className, style, }: DitherPulseRingProps): react_jsx_runtime.JSX.Element;
310
310
 
311
- export { AnimatedDrawingSVG, type CombineMode as CombineShaderMode, DitherPulseRing, EFECTO_ASCII_COMPONENT_DEFAULTS, EFECTO_ASCII_POST_PROCESSING_DEFAULTS, Efecto, type EfectoAsciiColorPalette, type EfectoAsciiStyle, type PublicPostProcessingSettings as EfectoPublicAsciiPostProcessingSettings, FractalFlower, MenuGlitch, type MenuGlitchUniforms, OranoParticles, type OranoParticlesUniforms, RippleWave, ShaderArt, type ShaderArtUniforms, Snow, WANDY_HAND_DEFAULTS, WandyHand };
311
+ type DitherStreamProps = {
312
+ width?: string | number;
313
+ height?: string | number;
314
+ className?: string;
315
+ style?: React.CSSProperties;
316
+ imageTextureSrc?: string;
317
+ projectionSpeed?: number;
318
+ beamSpeed?: number;
319
+ beamDirection?: "counterclockwise" | "clockwise";
320
+ beamColor?: [number, number, number] | string;
321
+ beamCenter?: [number, number];
322
+ beamRadius?: number;
323
+ beamScale?: number;
324
+ beamPathShape?: "circle" | "square" | "diamond" | "triangle" | "oval";
325
+ pathPos?: [number, number];
326
+ pathAngle?: number;
327
+ godrayIntensity?: number;
328
+ };
329
+ declare function DitherStream({ width, height, className, style, imageTextureSrc, projectionSpeed, beamSpeed, beamDirection, beamColor, beamCenter, beamRadius, beamScale, beamPathShape, pathPos, pathAngle, godrayIntensity, }: DitherStreamProps): react_jsx_runtime.JSX.Element;
330
+
331
+ export { AnimatedDrawingSVG, type CombineShaderMode, DitherPulseRing, DitherStream, EFECTO_ASCII_COMPONENT_DEFAULTS, EFECTO_ASCII_POST_PROCESSING_DEFAULTS, Efecto, type EfectoAsciiColorPalette, type EfectoAsciiStyle, type PublicPostProcessingSettings as EfectoPublicAsciiPostProcessingSettings, FractalFlower, MenuGlitch, type MenuGlitchUniforms, OranoParticles, type OranoParticlesUniforms, RippleWave, ShaderArt, type ShaderArtUniforms, Snow, WANDY_HAND_DEFAULTS, WandyHand };
package/dist/index.d.ts CHANGED
@@ -278,7 +278,7 @@ type NoiseWarpPassUniforms = {
278
278
  radius: number;
279
279
  };
280
280
 
281
- type CombineMode = "add" | "screen" | "multiply" | "overlay" | "max" | "min" | "difference" | "alphaOver" | "premultipliedOver" | "lerp" | "mask";
281
+ type CombineShaderMode = "add" | "screen" | "multiply" | "overlay" | "max" | "min" | "difference" | "alphaOver" | "premultipliedOver" | "lerp" | "mask";
282
282
 
283
283
  type DitherPulseRingProps = {
284
284
  spriteTextureSrc?: string;
@@ -286,7 +286,7 @@ type DitherPulseRingProps = {
286
286
  diffuseEnabled?: boolean;
287
287
  blurEnabled?: boolean;
288
288
  noiseWarpEnabled?: boolean;
289
- combineMode?: CombineMode;
289
+ combineMode?: CombineShaderMode;
290
290
  noiseWarpRadius?: NoiseWarpPassUniforms["radius"];
291
291
  noiseWarpStrength?: NoiseWarpPassUniforms["strength"];
292
292
  diffuseRadius?: DiffusePassUniforms["diffuseRadius"];
@@ -308,4 +308,24 @@ type DitherPulseRingProps = {
308
308
  };
309
309
  declare function DitherPulseRing({ spriteTextureSrc, glyphDitherEnabled, diffuseEnabled, blurEnabled, noiseWarpEnabled, combineMode, noiseWarpRadius, noiseWarpStrength, diffuseRadius, blurRadius, borderThickness, borderIntensity, borderColor, borderDitherStrength, borderTonemap, borderAlpha, ringColor, ringSpeed, ringPosition, ringAlpha, width, height, className, style, }: DitherPulseRingProps): react_jsx_runtime.JSX.Element;
310
310
 
311
- export { AnimatedDrawingSVG, type CombineMode as CombineShaderMode, DitherPulseRing, EFECTO_ASCII_COMPONENT_DEFAULTS, EFECTO_ASCII_POST_PROCESSING_DEFAULTS, Efecto, type EfectoAsciiColorPalette, type EfectoAsciiStyle, type PublicPostProcessingSettings as EfectoPublicAsciiPostProcessingSettings, FractalFlower, MenuGlitch, type MenuGlitchUniforms, OranoParticles, type OranoParticlesUniforms, RippleWave, ShaderArt, type ShaderArtUniforms, Snow, WANDY_HAND_DEFAULTS, WandyHand };
311
+ type DitherStreamProps = {
312
+ width?: string | number;
313
+ height?: string | number;
314
+ className?: string;
315
+ style?: React.CSSProperties;
316
+ imageTextureSrc?: string;
317
+ projectionSpeed?: number;
318
+ beamSpeed?: number;
319
+ beamDirection?: "counterclockwise" | "clockwise";
320
+ beamColor?: [number, number, number] | string;
321
+ beamCenter?: [number, number];
322
+ beamRadius?: number;
323
+ beamScale?: number;
324
+ beamPathShape?: "circle" | "square" | "diamond" | "triangle" | "oval";
325
+ pathPos?: [number, number];
326
+ pathAngle?: number;
327
+ godrayIntensity?: number;
328
+ };
329
+ declare function DitherStream({ width, height, className, style, imageTextureSrc, projectionSpeed, beamSpeed, beamDirection, beamColor, beamCenter, beamRadius, beamScale, beamPathShape, pathPos, pathAngle, godrayIntensity, }: DitherStreamProps): react_jsx_runtime.JSX.Element;
330
+
331
+ export { AnimatedDrawingSVG, type CombineShaderMode, DitherPulseRing, DitherStream, EFECTO_ASCII_COMPONENT_DEFAULTS, EFECTO_ASCII_POST_PROCESSING_DEFAULTS, Efecto, type EfectoAsciiColorPalette, type EfectoAsciiStyle, type PublicPostProcessingSettings as EfectoPublicAsciiPostProcessingSettings, FractalFlower, MenuGlitch, type MenuGlitchUniforms, OranoParticles, type OranoParticlesUniforms, RippleWave, ShaderArt, type ShaderArtUniforms, Snow, WANDY_HAND_DEFAULTS, WandyHand };
package/dist/index.js CHANGED
@@ -32,6 +32,7 @@ var src_exports = {};
32
32
  __export(src_exports, {
33
33
  AnimatedDrawingSVG: () => AnimatedDrawingSVG,
34
34
  DitherPulseRing: () => DitherPulseRing,
35
+ DitherStream: () => DitherStream,
35
36
  EFECTO_ASCII_COMPONENT_DEFAULTS: () => EFECTO_ASCII_COMPONENT_DEFAULTS,
36
37
  EFECTO_ASCII_POST_PROCESSING_DEFAULTS: () => EFECTO_ASCII_POST_PROCESSING_DEFAULTS,
37
38
  Efecto: () => Efecto,
@@ -4593,17 +4594,20 @@ function RenderPipeline({
4593
4594
  }) {
4594
4595
  const sharedScene = useSceneContext();
4595
4596
  const size = sharedScene?.size ?? { width: 1, height: 1 };
4597
+ const shouldCombine = Boolean(combine);
4596
4598
  const targets = (0, import_react19.useMemo)(() => {
4597
4599
  const entries = passes.map((_, index) => ({
4598
4600
  key: `pass_${index}`,
4599
4601
  target: new THREE16.WebGLRenderTarget(1, 1, DEFAULT_TARGET_OPTIONS)
4600
4602
  }));
4601
- entries.push({
4602
- key: "combine",
4603
- target: new THREE16.WebGLRenderTarget(1, 1, DEFAULT_TARGET_OPTIONS)
4604
- });
4603
+ if (shouldCombine) {
4604
+ entries.push({
4605
+ key: "combine",
4606
+ target: new THREE16.WebGLRenderTarget(1, 1, DEFAULT_TARGET_OPTIONS)
4607
+ });
4608
+ }
4605
4609
  return entries;
4606
- }, [passes]);
4610
+ }, [passes, shouldCombine]);
4607
4611
  (0, import_react19.useEffect)(() => {
4608
4612
  if (size.width <= 1 || size.height <= 1) return;
4609
4613
  targets.forEach(({ target }) => {
@@ -4633,7 +4637,7 @@ function RenderPipeline({
4633
4637
  if (!passes.length) return null;
4634
4638
  const lastEnabledIndex = [...passes].map((pass, index) => ({ pass, index })).filter(({ pass }) => pass.enabled !== false).map(({ index }) => index).pop();
4635
4639
  if (lastEnabledIndex === void 0) return null;
4636
- if (lastEnabledIndex <= Math.max(firstEnabledIndex, secondEnabledIndex)) {
4640
+ if (shouldCombine && lastEnabledIndex <= Math.max(firstEnabledIndex, secondEnabledIndex)) {
4637
4641
  return combineTarget ?? null;
4638
4642
  }
4639
4643
  return passOutputTarget(lastEnabledIndex);
@@ -4654,7 +4658,7 @@ function RenderPipeline({
4654
4658
  },
4655
4659
  `pass_${index}`
4656
4660
  );
4657
- if (!combineRendered && combineTarget && index === secondEnabledIndex) {
4661
+ if (shouldCombine && !combineRendered && combineTarget && index === secondEnabledIndex) {
4658
4662
  const inputA = passOutputTarget(firstEnabledIndex)?.texture ?? inputTexture;
4659
4663
  const inputB = passOutputTarget(secondEnabledIndex)?.texture ?? inputTexture;
4660
4664
  combineRendered = true;
@@ -4748,20 +4752,23 @@ function useMouse({
4748
4752
  const handleMouseLeave = () => {
4749
4753
  mouse.set(center?.x ?? 0.5, center?.y ?? 0.5);
4750
4754
  };
4751
- const moveTarget = element ?? window;
4752
- moveTarget.addEventListener("mousemove", handleMouseMove);
4753
- moveTarget.addEventListener("mouseleave", handleMouseLeave);
4754
4755
  if (element) {
4756
+ element.addEventListener("mousemove", handleMouseMove);
4757
+ element.addEventListener("mouseleave", handleMouseLeave);
4755
4758
  element.addEventListener("mouseenter", updateRect);
4759
+ } else if (hasWindow) {
4760
+ window.addEventListener("mousemove", handleMouseMove);
4756
4761
  }
4757
4762
  if (hasWindow) {
4758
4763
  window.addEventListener("resize", handleResize);
4759
4764
  }
4760
4765
  return () => {
4761
- moveTarget.removeEventListener("mousemove", handleMouseMove);
4762
- moveTarget.removeEventListener("mouseleave", handleMouseLeave);
4763
4766
  if (element) {
4767
+ element.removeEventListener("mousemove", handleMouseMove);
4768
+ element.removeEventListener("mouseleave", handleMouseLeave);
4764
4769
  element.removeEventListener("mouseenter", updateRect);
4770
+ } else if (hasWindow) {
4771
+ window.removeEventListener("mousemove", handleMouseMove);
4765
4772
  }
4766
4773
  if (hasWindow) {
4767
4774
  window.removeEventListener("resize", handleResize);
@@ -5128,10 +5135,469 @@ function DitherPulseRingContent({
5128
5135
  )
5129
5136
  ] });
5130
5137
  }
5138
+
5139
+ // src/components/DitherStreamBeamCompositePass.tsx
5140
+ var import_react23 = require("react");
5141
+ var THREE21 = __toESM(require("three"));
5142
+
5143
+ // src/utils/createFallbackTexture.ts
5144
+ var THREE20 = __toESM(require("three"));
5145
+ function createFallbackTexture() {
5146
+ const data = new Uint8Array([0, 0, 0, 255]);
5147
+ const texture = new THREE20.DataTexture(data, 1, 1, THREE20.RGBAFormat);
5148
+ texture.needsUpdate = true;
5149
+ texture.wrapS = THREE20.ClampToEdgeWrapping;
5150
+ texture.wrapT = THREE20.ClampToEdgeWrapping;
5151
+ texture.minFilter = THREE20.NearestFilter;
5152
+ texture.magFilter = THREE20.NearestFilter;
5153
+ return texture;
5154
+ }
5155
+
5156
+ // src/shaders/dither-godray-beam-composite/fragment.glsl
5157
+ var fragment_default15 = "precision highp float;\nprecision highp int;\n\nin vec2 vTextureCoord;\n\nuniform sampler2D uTexture;\nuniform float uTime;\nuniform vec2 uResolution;\nuniform float uBeamSpeed;\nuniform float uBeamDirection;\nuniform vec3 uBeamColor;\nuniform vec2 uBeamCenter;\nuniform float uBeamRadius;\nuniform float uBeamScale;\nuniform int uPathShape;\nuniform vec2 uPathPos;\nuniform float uPathAngle;\n\nout vec4 fragColor;\n\nconst float PI = 3.14159265;\nconst float TWO_PI = 6.28318531;\n\nuvec2 hash2d(uvec2 v) {\n v = v * 1664525u + 1013904223u;\n v.x += v.y * v.y * 1664525u + 1013904223u;\n v.y += v.x * v.x * 1664525u + 1013904223u;\n v ^= v >> 16u;\n v.x += v.y * v.y * 1664525u + 1013904223u;\n v.y += v.x * v.x * 1664525u + 1013904223u;\n return v;\n}\n\nfloat randomFibo(vec2 p) {\n uvec2 v = floatBitsToUint(p);\n v = hash2d(v);\n return float(v.x ^ v.y) / float(0xffffffffu);\n}\n\nfloat calculateAngle(vec2 p, vec2 c) {\n float a = atan(p.y - c.y, p.x - c.x);\n return a < 0.0 ? a + TWO_PI : a;\n}\n\nfloat angularDiff(float a, float b) {\n float d = abs(a - b);\n return d > PI ? TWO_PI - d : d;\n}\n\nfloat angularFade(float pointAngle, float peakAngle, float fadeAmount) {\n return 1.04 - smoothstep(0.0, fadeAmount, angularDiff(pointAngle, peakAngle));\n}\n\nfloat sdEquilateralTriangle(vec2 p) {\n const float k = 1.7320508;\n p.x = abs(p.x) - 1.0;\n p.y = p.y + 1.0 / k;\n if (p.x + k * p.y > 0.0) {\n p = vec2(p.x - k * p.y, -k * p.x - p.y) / 2.0;\n }\n p.x -= clamp(p.x, -2.0, 0.0);\n return -length(p) * sign(p.y);\n}\n\nvec3 dodge(vec3 src, vec3 dst) {\n return vec3(\n src.x >= 1.0 ? 1.0 : min(1.0, dst.x / max(0.001, 1.0 - src.x)),\n src.y >= 1.0 ? 1.0 : min(1.0, dst.y / max(0.001, 1.0 - src.y)),\n src.z >= 1.0 ? 1.0 : min(1.0, dst.z / max(0.001, 1.0 - src.z))\n );\n}\n\nfloat easeExpoIn(float t) {\n return t <= 0.0 ? 0.0 : pow(2.0, 10.0 * (t - 1.0));\n}\n\nvec2 rot2(vec2 v, float a) {\n float c = cos(a);\n float s = sin(a);\n return vec2(v.x * c - v.y * s, v.x * s + v.y * c);\n}\n\nvec3 beamAt(vec2 uv) {\n float aspect = uResolution.x / uResolution.y;\n vec2 center = uBeamCenter;\n\n vec2 u2 = vec2(uv.x * aspect, uv.y);\n vec2 c2 = vec2(center.x * aspect, center.y);\n\n float ringRadius = uBeamRadius;\n vec2 delta = (u2 - c2) / max(uBeamScale, 0.0001);\n float circleDist = abs(length(delta) - ringRadius);\n float squareDist = abs(max(abs(delta.x), abs(delta.y)) - ringRadius);\n float diamondDist = abs(abs(delta.x) + abs(delta.y) - ringRadius);\n float ovalDist =\n abs(length(vec2(delta.x / 1.5, delta.y)) - ringRadius);\n float triangleDist =\n abs(sdEquilateralTriangle(delta / max(ringRadius, 0.0001))) * ringRadius;\n\n float ringDist = circleDist;\n if (uPathShape == 1) {\n ringDist = squareDist;\n } else if (uPathShape == 2) {\n ringDist = diamondDist;\n } else if (uPathShape == 3) {\n ringDist = triangleDist;\n } else if (uPathShape == 4) {\n ringDist = ovalDist;\n }\n\n float b = 0.25 / (1.0 - smoothstep(0.2, 0.002, ringDist + 0.02));\n float ang = fract(0.19 + uTime * uBeamSpeed * uBeamDirection) * TWO_PI;\n b *= angularFade(calculateAngle(u2, c2), ang, PI * 0.5);\n\n vec3 col = b * pow(max(0.0, 1.0 - ringDist), 3.0) * uBeamColor;\n col = tanh(clamp(col, -40.0, 40.0));\n col += (randomFibo(gl_FragCoord.xy) - 0.5) / 255.0;\n\n return col;\n}\n\nvoid main() {\n vec2 uv = vTextureCoord;\n\n float ang = uPathAngle;\n vec2 pos = uPathPos;\n vec2 off = uv - pos;\n vec2 ro = rot2(off, -ang);\n vec2 so = ro;\n\n if (ro.x > 0.0) {\n float e = easeExpoIn(ro.x);\n so.y = ro.y / (1.0 + 4.0 * e * e);\n }\n\n vec2 st = clamp(pos + rot2(so, ang), 0.0, 1.0);\n\n vec3 beam = beamAt(st);\n vec4 img = texture(uTexture, st);\n vec3 outColor = mix(beam, dodge(img.rgb, beam), img.a);\n\n fragColor = vec4(outColor, 1.0);\n}\n";
5158
+
5159
+ // src/shaders/dither-godray-beam-composite/vertex.glsl
5160
+ var vertex_default14 = "out vec2 vTextureCoord;\n\nvoid main() {\n vTextureCoord = uv;\n gl_Position = vec4(position, 1.0);\n}\n";
5161
+
5162
+ // src/components/DitherStreamBeamCompositePass.tsx
5163
+ var import_jsx_runtime24 = require("react/jsx-runtime");
5164
+ function DitherStreamBeamCompositePass({
5165
+ inputTexture = null,
5166
+ beamSpeed = 0.1,
5167
+ beamDirection = "counterclockwise",
5168
+ beamColor = [0.667, 0.686, 0.941],
5169
+ beamCenter = [0.5, 0.95],
5170
+ beamRadius = 0.6,
5171
+ beamScale = 1,
5172
+ pathShape = "circle",
5173
+ pathPos = [0.5009, 1.0473],
5174
+ pathAngle = (0.999 - 0.25) * -6.28318531,
5175
+ target = null,
5176
+ clear = true,
5177
+ enabled = true,
5178
+ priority = 0
5179
+ }) {
5180
+ const fallbackTexture = (0, import_react23.useMemo)(() => createFallbackTexture(), []);
5181
+ const uniforms = (0, import_react23.useMemo)(
5182
+ () => ({
5183
+ uTexture: { value: inputTexture ?? fallbackTexture },
5184
+ uTime: { value: 0 },
5185
+ uResolution: { value: new THREE21.Vector2(1, 1) },
5186
+ uBeamSpeed: { value: beamSpeed },
5187
+ uBeamDirection: { value: beamDirection === "clockwise" ? -1 : 1 },
5188
+ uBeamColor: {
5189
+ value: new THREE21.Color(beamColor[0], beamColor[1], beamColor[2])
5190
+ },
5191
+ uBeamCenter: { value: new THREE21.Vector2(beamCenter[0], beamCenter[1]) },
5192
+ uBeamRadius: { value: beamRadius },
5193
+ uBeamScale: { value: beamScale },
5194
+ uPathShape: {
5195
+ value: pathShape === "square" ? 1 : pathShape === "diamond" ? 2 : pathShape === "triangle" ? 3 : pathShape === "oval" ? 4 : 0
5196
+ },
5197
+ uPathPos: { value: new THREE21.Vector2(pathPos[0], pathPos[1]) },
5198
+ uPathAngle: { value: pathAngle }
5199
+ }),
5200
+ // eslint-disable-next-line react-hooks/exhaustive-deps
5201
+ []
5202
+ );
5203
+ uniforms.uTexture.value = inputTexture ?? fallbackTexture;
5204
+ uniforms.uBeamSpeed.value = beamSpeed;
5205
+ uniforms.uBeamDirection.value = beamDirection === "clockwise" ? -1 : 1;
5206
+ uniforms.uBeamColor.value.setRGB(
5207
+ beamColor[0],
5208
+ beamColor[1],
5209
+ beamColor[2]
5210
+ );
5211
+ uniforms.uBeamCenter.value.set(
5212
+ beamCenter[0],
5213
+ beamCenter[1]
5214
+ );
5215
+ uniforms.uBeamRadius.value = beamRadius;
5216
+ uniforms.uBeamScale.value = beamScale;
5217
+ uniforms.uPathShape.value = pathShape === "square" ? 1 : pathShape === "diamond" ? 2 : pathShape === "triangle" ? 3 : pathShape === "oval" ? 4 : 0;
5218
+ uniforms.uPathPos.value.set(pathPos[0], pathPos[1]);
5219
+ uniforms.uPathAngle.value = pathAngle;
5220
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5221
+ ShaderPass,
5222
+ {
5223
+ vertexShader: vertex_default14,
5224
+ fragmentShader: fragment_default15,
5225
+ uniforms,
5226
+ inputTexture: inputTexture ?? fallbackTexture,
5227
+ target,
5228
+ clear,
5229
+ enabled,
5230
+ priority,
5231
+ timeUniform: "uTime",
5232
+ resolutionUniform: "uResolution",
5233
+ blending: THREE21.NoBlending
5234
+ }
5235
+ );
5236
+ }
5237
+
5238
+ // src/components/DitherStreamDitherPass.tsx
5239
+ var import_react24 = require("react");
5240
+ var THREE22 = __toESM(require("three"));
5241
+
5242
+ // src/shaders/dither-godray-dither/fragment.glsl
5243
+ var fragment_default16 = "precision highp float;\n\nin vec2 vTextureCoord;\n\nuniform sampler2D uTexture;\nuniform vec2 uResolution;\n\nout vec4 fragColor;\n\nvoid main() {\n vec2 uv = vTextureCoord;\n\n float ar = uResolution.x / uResolution.y;\n float ac = mix(ar, 1.0 / ar, 0.5);\n\n float gs = 0.005;\n float bg = 1.0 / gs;\n vec2 cellSize = vec2(1.0 / (bg * ar), 1.0 / bg) * ac;\n\n vec2 pos = vec2(0.5);\n vec2 off = uv - pos;\n vec2 cell = floor(off / cellSize);\n vec2 center = (cell + 0.5) * cellSize;\n vec2 pixelUv = center + pos;\n\n vec4 c = texture(uTexture, pixelUv);\n float lum = dot(c.rgb, vec3(0.2126, 0.7152, 0.0722));\n float gm = pow(mix(0.2, 2.2, 0.3), 2.2);\n\n vec2 local = mod(uv - pos, cellSize) / cellSize;\n vec2 ct = local * 2.0 - 1.0;\n float d = length(ct);\n\n float ns = 16.0;\n float si = clamp(floor(lum * ns * gm), 0.0, ns - 1.0);\n float rad = si / ns;\n float alpha = smoothstep(rad + 0.08, rad - 0.08, d);\n\n vec3 tint = (c.rgb - si * 0.04) * 1.4;\n fragColor = vec4(mix(vec3(0.0), tint, alpha), 1.0);\n}\n";
5244
+
5245
+ // src/shaders/dither-godray-dither/vertex.glsl
5246
+ var vertex_default15 = "out vec2 vTextureCoord;\n\nvoid main() {\n vTextureCoord = uv;\n gl_Position = vec4(position, 1.0);\n}\n";
5247
+
5248
+ // src/components/DitherStreamDitherPass.tsx
5249
+ var import_jsx_runtime25 = require("react/jsx-runtime");
5250
+ function DitherStreamDitherPass({
5251
+ inputTexture = null,
5252
+ target = null,
5253
+ clear = true,
5254
+ enabled = true,
5255
+ priority = 0
5256
+ }) {
5257
+ const fallbackTexture = (0, import_react24.useMemo)(() => createFallbackTexture(), []);
5258
+ const uniforms = (0, import_react24.useMemo)(
5259
+ () => ({
5260
+ uTexture: { value: inputTexture ?? fallbackTexture },
5261
+ uResolution: { value: new THREE22.Vector2(1, 1) }
5262
+ }),
5263
+ // eslint-disable-next-line react-hooks/exhaustive-deps
5264
+ []
5265
+ );
5266
+ uniforms.uTexture.value = inputTexture ?? fallbackTexture;
5267
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5268
+ ShaderPass,
5269
+ {
5270
+ vertexShader: vertex_default15,
5271
+ fragmentShader: fragment_default16,
5272
+ uniforms,
5273
+ inputTexture: inputTexture ?? fallbackTexture,
5274
+ target,
5275
+ clear,
5276
+ enabled,
5277
+ priority,
5278
+ resolutionUniform: "uResolution",
5279
+ blending: THREE22.NoBlending
5280
+ }
5281
+ );
5282
+ }
5283
+
5284
+ // src/components/DitherStreamGodRaysPass.tsx
5285
+ var import_react25 = require("react");
5286
+ var THREE23 = __toESM(require("three"));
5287
+
5288
+ // src/shaders/dither-godray-extract/fragment.glsl
5289
+ var fragment_default17 = "precision highp float;\n\nin vec2 vTextureCoord;\nuniform sampler2D uTexture;\nout vec4 fragColor;\n\nvoid main() {\n vec4 c = texture(uTexture, vTextureCoord);\n float l = dot(c.rgb, vec3(0.299, 0.587, 0.114));\n fragColor = c * smoothstep(-0.1, 0.0, l);\n}\n";
5290
+
5291
+ // src/shaders/dither-godray-extract/vertex.glsl
5292
+ var vertex_default16 = "out vec2 vTextureCoord;\n\nvoid main() {\n vTextureCoord = uv;\n gl_Position = vec4(position, 1.0);\n}\n";
5293
+
5294
+ // src/shaders/dither-godray-final/fragment.glsl
5295
+ var fragment_default18 = "precision highp float;\n\nin vec2 vTextureCoord;\nuniform sampler2D uScene;\nuniform sampler2D uGR;\nuniform float uGodrayIntensity;\nout vec4 fragColor;\n\nvoid main() {\n vec3 bg = texture(uScene, vTextureCoord).rgb;\n vec3 gr = texture(uGR, vTextureCoord).rgb;\n fragColor = vec4(bg + gr * uGodrayIntensity, 1.0);\n}\n";
5296
+
5297
+ // src/shaders/dither-godray-final/vertex.glsl
5298
+ var vertex_default17 = "out vec2 vTextureCoord;\n\nvoid main() {\n vTextureCoord = uv;\n gl_Position = vec4(position, 1.0);\n}\n";
5299
+
5300
+ // src/shaders/dither-godray-march/fragment.glsl
5301
+ var fragment_default19 = "precision highp float;\n\nin vec2 vTextureCoord;\nuniform sampler2D uTexture;\nout vec4 fragColor;\n\nvoid main() {\n vec3 col = vec3(0.0);\n vec2 pos = vec2(0.5);\n vec2 dir = (pos - vTextureCoord) * 0.02;\n float w = 1.0;\n vec2 tc = vTextureCoord;\n\n for (int i = 0; i < 16; i++) {\n col += texture(uTexture, tc).rgb * w;\n w *= 0.94;\n tc += dir;\n }\n\n fragColor = vec4(col / 16.0, length(tc - vTextureCoord));\n}\n";
5302
+
5303
+ // src/shaders/dither-godray-march/vertex.glsl
5304
+ var vertex_default18 = "out vec2 vTextureCoord;\n\nvoid main() {\n vTextureCoord = uv;\n gl_Position = vec4(position, 1.0);\n}\n";
5305
+
5306
+ // src/components/DitherStreamGodRaysPass.tsx
5307
+ var import_jsx_runtime26 = require("react/jsx-runtime");
5308
+ var TARGET_OPTIONS = {
5309
+ depthBuffer: false,
5310
+ stencilBuffer: false
5311
+ };
5312
+ function DitherStreamGodRaysPass({
5313
+ inputTexture = null,
5314
+ intensity = 2.9,
5315
+ target = null,
5316
+ enabled = true,
5317
+ priority = 0
5318
+ }) {
5319
+ const sharedScene = useSceneContext();
5320
+ const size = sharedScene?.size ?? { width: 1, height: 1 };
5321
+ const fallbackTexture = (0, import_react25.useMemo)(() => createFallbackTexture(), []);
5322
+ const extractTarget = (0, import_react25.useMemo)(
5323
+ () => new THREE23.WebGLRenderTarget(1, 1, TARGET_OPTIONS),
5324
+ []
5325
+ );
5326
+ const marchTarget = (0, import_react25.useMemo)(
5327
+ () => new THREE23.WebGLRenderTarget(1, 1, TARGET_OPTIONS),
5328
+ []
5329
+ );
5330
+ (0, import_react25.useEffect)(() => {
5331
+ return () => {
5332
+ extractTarget.dispose();
5333
+ marchTarget.dispose();
5334
+ };
5335
+ }, [extractTarget, marchTarget]);
5336
+ (0, import_react25.useEffect)(() => {
5337
+ if (size.width <= 1 || size.height <= 1) return;
5338
+ extractTarget.setSize(size.width, size.height);
5339
+ marchTarget.setSize(
5340
+ Math.max(1, Math.floor(size.width / 4)),
5341
+ Math.max(1, Math.floor(size.height / 4))
5342
+ );
5343
+ }, [extractTarget, marchTarget, size.height, size.width]);
5344
+ const extractUniforms = (0, import_react25.useMemo)(
5345
+ () => ({
5346
+ uTexture: { value: inputTexture ?? fallbackTexture }
5347
+ }),
5348
+ // eslint-disable-next-line react-hooks/exhaustive-deps
5349
+ []
5350
+ );
5351
+ const marchUniforms = (0, import_react25.useMemo)(
5352
+ () => ({
5353
+ uTexture: { value: extractTarget.texture }
5354
+ }),
5355
+ // eslint-disable-next-line react-hooks/exhaustive-deps
5356
+ []
5357
+ );
5358
+ const finalUniforms = (0, import_react25.useMemo)(
5359
+ () => ({
5360
+ uScene: { value: inputTexture ?? fallbackTexture },
5361
+ uGR: { value: marchTarget.texture },
5362
+ uGodrayIntensity: { value: intensity }
5363
+ }),
5364
+ // eslint-disable-next-line react-hooks/exhaustive-deps
5365
+ []
5366
+ );
5367
+ extractUniforms.uTexture.value = inputTexture ?? fallbackTexture;
5368
+ marchUniforms.uTexture.value = extractTarget.texture;
5369
+ finalUniforms.uScene.value = inputTexture ?? fallbackTexture;
5370
+ finalUniforms.uGR.value = marchTarget.texture;
5371
+ finalUniforms.uGodrayIntensity.value = intensity;
5372
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
5373
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5374
+ ShaderPass,
5375
+ {
5376
+ vertexShader: vertex_default16,
5377
+ fragmentShader: fragment_default17,
5378
+ uniforms: extractUniforms,
5379
+ inputTexture: inputTexture ?? fallbackTexture,
5380
+ target: extractTarget,
5381
+ clear: true,
5382
+ enabled,
5383
+ priority,
5384
+ blending: THREE23.NoBlending
5385
+ }
5386
+ ),
5387
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5388
+ ShaderPass,
5389
+ {
5390
+ vertexShader: vertex_default18,
5391
+ fragmentShader: fragment_default19,
5392
+ uniforms: marchUniforms,
5393
+ inputTexture: extractTarget.texture,
5394
+ target: marchTarget,
5395
+ clear: true,
5396
+ enabled,
5397
+ priority: priority + 1,
5398
+ blending: THREE23.NoBlending
5399
+ }
5400
+ ),
5401
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5402
+ ShaderPass,
5403
+ {
5404
+ vertexShader: vertex_default17,
5405
+ fragmentShader: fragment_default18,
5406
+ uniforms: finalUniforms,
5407
+ inputTexture: inputTexture ?? fallbackTexture,
5408
+ target,
5409
+ clear: true,
5410
+ enabled,
5411
+ priority: priority + 2,
5412
+ blending: THREE23.NoBlending
5413
+ }
5414
+ )
5415
+ ] });
5416
+ }
5417
+
5418
+ // src/components/DitherStreamProjectionPass.tsx
5419
+ var import_react26 = require("react");
5420
+ var THREE24 = __toESM(require("three"));
5421
+
5422
+ // src/shaders/dither-godray-projection/fragment.glsl
5423
+ var fragment_default20 = "precision highp float;\n\nin vec2 vTextureCoord;\n\nuniform sampler2D uTex;\nuniform float uTime;\nuniform float uProjectionSpeed;\n\nout vec4 fragColor;\n\nconst float PI = 3.14159265;\n\nvec3 ray(vec2 uv, vec2 m, float a) {\n vec2 s = (uv - 0.5) * 2.0;\n s.x *= a;\n s.y *= -1.0;\n\n float f = mix(radians(20.0), radians(120.0), 0.38);\n vec3 r = normalize(vec3(s * tan(f / 2.0), -1.0));\n\n float rx = (m.y - 0.5) * PI;\n float ry = (m.x - 0.5) * PI * 2.0;\n mat3 rY = mat3(cos(ry), 0.0, -sin(ry), 0.0, 1.0, 0.0, sin(ry), 0.0, cos(ry));\n mat3 rX = mat3(1.0, 0.0, 0.0, 0.0, cos(rx), sin(rx), 0.0, -sin(rx), cos(rx));\n\n return normalize(rX * rY * r);\n}\n\nvec2 directionToUv(vec3 d) {\n return vec2(atan(d.z, d.x) / (2.0 * PI) + 0.75, acos(clamp(d.y, -1.0, 1.0)) / PI);\n}\n\nvec4 sampleRepeat(vec2 uv) {\n vec2 f = vec2(uv.x, fract(uv.y));\n vec4 c = texture(uTex, f);\n\n float blendWidth = 0.1;\n float blendFactor = 0.0;\n if (f.y < blendWidth) {\n blendFactor = 1.0 - f.y / blendWidth;\n } else if (f.y > 1.0 - blendWidth) {\n blendFactor = (f.y - (1.0 - blendWidth)) / blendWidth;\n }\n\n if (blendFactor > 0.0) {\n blendFactor = smoothstep(0.0, 1.0, blendFactor);\n vec2 opposite = vec2(f.x, f.y > 0.5 ? f.y - 0.5 : f.y + 0.5);\n c = mix(c, texture(uTex, opposite), blendFactor);\n }\n\n return c;\n}\n\nvoid main() {\n vec2 uv = vTextureCoord;\n\n vec3 rd = ray(uv, vec2(0.5, 0.5056), 2.0);\n vec2 s = directionToUv(rd);\n\n float f = mix(radians(20.0), radians(120.0), 0.38);\n s = (s - 0.5) * (2.0 / tan(f / 2.0)) + 0.5;\n s += vec2(0.0, 0.02) * uTime * uProjectionSpeed;\n\n vec4 c = sampleRepeat(s);\n c.rgb = clamp(c.rgb - 0.14, 0.0, 1.0);\n\n float l = dot(c.rgb, vec3(0.299, 0.587, 0.114));\n c.rgb = mix(vec3(l), c.rgb, 0.79);\n c.rgb = 1.22 * (c.rgb - 0.5) + 0.5;\n c.rgb = clamp(c.rgb, 0.0, 1.0);\n\n fragColor = c;\n}\n";
5424
+
5425
+ // src/shaders/dither-godray-projection/vertex.glsl
5426
+ var vertex_default19 = "out vec2 vTextureCoord;\n\nvoid main() {\n vTextureCoord = uv;\n gl_Position = vec4(position, 1.0);\n}\n";
5427
+
5428
+ // src/components/DitherStreamProjectionPass.tsx
5429
+ var import_jsx_runtime27 = require("react/jsx-runtime");
5430
+ var DEFAULT_IMAGE_SRC = "https://firebasestorage.googleapis.com/v0/b/unicorn-studio.appspot.com/o/Zz28X5RDkvcGGVYLr9X6QdTIhxy1%2FUntitled%20design%20-%202025-10-14T141250.707.webp?alt=media&token=bfcd11a8-6529-41a6-a592-d78147e93840";
5431
+ function DitherStreamProjectionPass({
5432
+ imageTextureSrc = DEFAULT_IMAGE_SRC,
5433
+ projectionSpeed = 0.05,
5434
+ target = null,
5435
+ clear = true,
5436
+ enabled = true,
5437
+ priority = 0
5438
+ }) {
5439
+ const [imageTexture, setImageTexture] = (0, import_react26.useState)(null);
5440
+ const fallbackTexture = (0, import_react26.useMemo)(() => createFallbackTexture(), []);
5441
+ (0, import_react26.useEffect)(() => {
5442
+ let active = true;
5443
+ const loader = new THREE24.TextureLoader();
5444
+ loader.load(
5445
+ imageTextureSrc,
5446
+ (texture) => {
5447
+ if (!active) {
5448
+ texture.dispose();
5449
+ return;
5450
+ }
5451
+ texture.wrapS = THREE24.RepeatWrapping;
5452
+ texture.wrapT = THREE24.RepeatWrapping;
5453
+ texture.minFilter = THREE24.LinearFilter;
5454
+ texture.magFilter = THREE24.LinearFilter;
5455
+ texture.generateMipmaps = true;
5456
+ texture.colorSpace = THREE24.NoColorSpace;
5457
+ texture.needsUpdate = true;
5458
+ setImageTexture(texture);
5459
+ },
5460
+ void 0,
5461
+ () => {
5462
+ if (!active) return;
5463
+ setImageTexture(fallbackTexture);
5464
+ }
5465
+ );
5466
+ return () => {
5467
+ active = false;
5468
+ };
5469
+ }, [fallbackTexture, imageTextureSrc]);
5470
+ const uniforms = (0, import_react26.useMemo)(
5471
+ () => ({
5472
+ uTex: { value: imageTexture ?? fallbackTexture },
5473
+ uTime: { value: 0 },
5474
+ uProjectionSpeed: { value: projectionSpeed }
5475
+ }),
5476
+ // eslint-disable-next-line react-hooks/exhaustive-deps
5477
+ []
5478
+ );
5479
+ uniforms.uTex.value = imageTexture ?? fallbackTexture;
5480
+ uniforms.uProjectionSpeed.value = projectionSpeed;
5481
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5482
+ ShaderPass,
5483
+ {
5484
+ vertexShader: vertex_default19,
5485
+ fragmentShader: fragment_default20,
5486
+ uniforms,
5487
+ target,
5488
+ clear,
5489
+ enabled,
5490
+ priority,
5491
+ timeUniform: "uTime",
5492
+ blending: THREE24.NoBlending
5493
+ }
5494
+ );
5495
+ }
5496
+
5497
+ // src/components/DitherStreamRendererConfig.tsx
5498
+ var import_react27 = require("react");
5499
+ var THREE25 = __toESM(require("three"));
5500
+ function DitherStreamRendererConfig() {
5501
+ const sharedScene = useSceneContext();
5502
+ (0, import_react27.useEffect)(() => {
5503
+ const context = sharedScene?.contextRef.current;
5504
+ if (!context) return;
5505
+ const renderer = context.renderer;
5506
+ const prevOutputColorSpace = renderer.outputColorSpace;
5507
+ const prevToneMapping = renderer.toneMapping;
5508
+ renderer.outputColorSpace = THREE25.LinearSRGBColorSpace;
5509
+ renderer.toneMapping = THREE25.NoToneMapping;
5510
+ return () => {
5511
+ renderer.outputColorSpace = prevOutputColorSpace;
5512
+ renderer.toneMapping = prevToneMapping;
5513
+ };
5514
+ }, [sharedScene]);
5515
+ return null;
5516
+ }
5517
+
5518
+ // src/components/DitherStream.tsx
5519
+ var import_jsx_runtime28 = require("react/jsx-runtime");
5520
+ function DitherStream({
5521
+ width = "100%",
5522
+ height = "100%",
5523
+ className = "relative h-full w-full",
5524
+ style,
5525
+ imageTextureSrc,
5526
+ projectionSpeed = 0.05,
5527
+ beamSpeed = 0.1,
5528
+ beamDirection = "counterclockwise",
5529
+ beamColor = [0.667, 0.686, 0.941],
5530
+ beamCenter = [0.5, 0.95],
5531
+ beamRadius = 0.6,
5532
+ beamScale = 1,
5533
+ beamPathShape = "circle",
5534
+ pathPos = [0.5009, 1.0473],
5535
+ pathAngle = (0.999 - 0.25) * -6.28318531,
5536
+ godrayIntensity = 2.9
5537
+ }) {
5538
+ const resolveColor = (color) => {
5539
+ if (Array.isArray(color)) return color;
5540
+ const normalized = color.replace("#", "");
5541
+ if (normalized.length !== 6)
5542
+ return [0.667, 0.686, 0.941];
5543
+ return [
5544
+ parseInt(normalized.slice(0, 2), 16) / 255,
5545
+ parseInt(normalized.slice(2, 4), 16) / 255,
5546
+ parseInt(normalized.slice(4, 6), 16) / 255
5547
+ ];
5548
+ };
5549
+ const resolvedBeamColor = resolveColor(beamColor);
5550
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
5551
+ SceneProvider,
5552
+ {
5553
+ width,
5554
+ height,
5555
+ className,
5556
+ style,
5557
+ manualRender: true,
5558
+ children: [
5559
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(DitherStreamRendererConfig, {}),
5560
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
5561
+ RenderPipeline,
5562
+ {
5563
+ passes: [
5564
+ {
5565
+ component: DitherStreamProjectionPass,
5566
+ props: { imageTextureSrc, projectionSpeed }
5567
+ },
5568
+ {
5569
+ component: DitherStreamBeamCompositePass,
5570
+ props: {
5571
+ beamSpeed,
5572
+ beamDirection,
5573
+ beamColor: resolvedBeamColor,
5574
+ beamCenter,
5575
+ beamRadius,
5576
+ beamScale,
5577
+ pathShape: beamPathShape,
5578
+ pathPos,
5579
+ pathAngle
5580
+ }
5581
+ },
5582
+ {
5583
+ component: DitherStreamDitherPass
5584
+ },
5585
+ {
5586
+ component: DitherStreamGodRaysPass,
5587
+ props: { intensity: godrayIntensity }
5588
+ }
5589
+ ]
5590
+ }
5591
+ )
5592
+ ]
5593
+ }
5594
+ );
5595
+ }
5131
5596
  // Annotate the CommonJS export names for ESM import in node:
5132
5597
  0 && (module.exports = {
5133
5598
  AnimatedDrawingSVG,
5134
5599
  DitherPulseRing,
5600
+ DitherStream,
5135
5601
  EFECTO_ASCII_COMPONENT_DEFAULTS,
5136
5602
  EFECTO_ASCII_POST_PROCESSING_DEFAULTS,
5137
5603
  Efecto,
package/dist/index.mjs CHANGED
@@ -4580,17 +4580,20 @@ function RenderPipeline({
4580
4580
  }) {
4581
4581
  const sharedScene = useSceneContext();
4582
4582
  const size = sharedScene?.size ?? { width: 1, height: 1 };
4583
+ const shouldCombine = Boolean(combine);
4583
4584
  const targets = useMemo12(() => {
4584
4585
  const entries = passes.map((_, index) => ({
4585
4586
  key: `pass_${index}`,
4586
4587
  target: new THREE16.WebGLRenderTarget(1, 1, DEFAULT_TARGET_OPTIONS)
4587
4588
  }));
4588
- entries.push({
4589
- key: "combine",
4590
- target: new THREE16.WebGLRenderTarget(1, 1, DEFAULT_TARGET_OPTIONS)
4591
- });
4589
+ if (shouldCombine) {
4590
+ entries.push({
4591
+ key: "combine",
4592
+ target: new THREE16.WebGLRenderTarget(1, 1, DEFAULT_TARGET_OPTIONS)
4593
+ });
4594
+ }
4592
4595
  return entries;
4593
- }, [passes]);
4596
+ }, [passes, shouldCombine]);
4594
4597
  useEffect14(() => {
4595
4598
  if (size.width <= 1 || size.height <= 1) return;
4596
4599
  targets.forEach(({ target }) => {
@@ -4620,7 +4623,7 @@ function RenderPipeline({
4620
4623
  if (!passes.length) return null;
4621
4624
  const lastEnabledIndex = [...passes].map((pass, index) => ({ pass, index })).filter(({ pass }) => pass.enabled !== false).map(({ index }) => index).pop();
4622
4625
  if (lastEnabledIndex === void 0) return null;
4623
- if (lastEnabledIndex <= Math.max(firstEnabledIndex, secondEnabledIndex)) {
4626
+ if (shouldCombine && lastEnabledIndex <= Math.max(firstEnabledIndex, secondEnabledIndex)) {
4624
4627
  return combineTarget ?? null;
4625
4628
  }
4626
4629
  return passOutputTarget(lastEnabledIndex);
@@ -4641,7 +4644,7 @@ function RenderPipeline({
4641
4644
  },
4642
4645
  `pass_${index}`
4643
4646
  );
4644
- if (!combineRendered && combineTarget && index === secondEnabledIndex) {
4647
+ if (shouldCombine && !combineRendered && combineTarget && index === secondEnabledIndex) {
4645
4648
  const inputA = passOutputTarget(firstEnabledIndex)?.texture ?? inputTexture;
4646
4649
  const inputB = passOutputTarget(secondEnabledIndex)?.texture ?? inputTexture;
4647
4650
  combineRendered = true;
@@ -4735,20 +4738,23 @@ function useMouse({
4735
4738
  const handleMouseLeave = () => {
4736
4739
  mouse.set(center?.x ?? 0.5, center?.y ?? 0.5);
4737
4740
  };
4738
- const moveTarget = element ?? window;
4739
- moveTarget.addEventListener("mousemove", handleMouseMove);
4740
- moveTarget.addEventListener("mouseleave", handleMouseLeave);
4741
4741
  if (element) {
4742
+ element.addEventListener("mousemove", handleMouseMove);
4743
+ element.addEventListener("mouseleave", handleMouseLeave);
4742
4744
  element.addEventListener("mouseenter", updateRect);
4745
+ } else if (hasWindow) {
4746
+ window.addEventListener("mousemove", handleMouseMove);
4743
4747
  }
4744
4748
  if (hasWindow) {
4745
4749
  window.addEventListener("resize", handleResize);
4746
4750
  }
4747
4751
  return () => {
4748
- moveTarget.removeEventListener("mousemove", handleMouseMove);
4749
- moveTarget.removeEventListener("mouseleave", handleMouseLeave);
4750
4752
  if (element) {
4753
+ element.removeEventListener("mousemove", handleMouseMove);
4754
+ element.removeEventListener("mouseleave", handleMouseLeave);
4751
4755
  element.removeEventListener("mouseenter", updateRect);
4756
+ } else if (hasWindow) {
4757
+ window.removeEventListener("mousemove", handleMouseMove);
4752
4758
  }
4753
4759
  if (hasWindow) {
4754
4760
  window.removeEventListener("resize", handleResize);
@@ -5115,9 +5121,468 @@ function DitherPulseRingContent({
5115
5121
  )
5116
5122
  ] });
5117
5123
  }
5124
+
5125
+ // src/components/DitherStreamBeamCompositePass.tsx
5126
+ import { useMemo as useMemo16 } from "react";
5127
+ import * as THREE21 from "three";
5128
+
5129
+ // src/utils/createFallbackTexture.ts
5130
+ import * as THREE20 from "three";
5131
+ function createFallbackTexture() {
5132
+ const data = new Uint8Array([0, 0, 0, 255]);
5133
+ const texture = new THREE20.DataTexture(data, 1, 1, THREE20.RGBAFormat);
5134
+ texture.needsUpdate = true;
5135
+ texture.wrapS = THREE20.ClampToEdgeWrapping;
5136
+ texture.wrapT = THREE20.ClampToEdgeWrapping;
5137
+ texture.minFilter = THREE20.NearestFilter;
5138
+ texture.magFilter = THREE20.NearestFilter;
5139
+ return texture;
5140
+ }
5141
+
5142
+ // src/shaders/dither-godray-beam-composite/fragment.glsl
5143
+ var fragment_default15 = "precision highp float;\nprecision highp int;\n\nin vec2 vTextureCoord;\n\nuniform sampler2D uTexture;\nuniform float uTime;\nuniform vec2 uResolution;\nuniform float uBeamSpeed;\nuniform float uBeamDirection;\nuniform vec3 uBeamColor;\nuniform vec2 uBeamCenter;\nuniform float uBeamRadius;\nuniform float uBeamScale;\nuniform int uPathShape;\nuniform vec2 uPathPos;\nuniform float uPathAngle;\n\nout vec4 fragColor;\n\nconst float PI = 3.14159265;\nconst float TWO_PI = 6.28318531;\n\nuvec2 hash2d(uvec2 v) {\n v = v * 1664525u + 1013904223u;\n v.x += v.y * v.y * 1664525u + 1013904223u;\n v.y += v.x * v.x * 1664525u + 1013904223u;\n v ^= v >> 16u;\n v.x += v.y * v.y * 1664525u + 1013904223u;\n v.y += v.x * v.x * 1664525u + 1013904223u;\n return v;\n}\n\nfloat randomFibo(vec2 p) {\n uvec2 v = floatBitsToUint(p);\n v = hash2d(v);\n return float(v.x ^ v.y) / float(0xffffffffu);\n}\n\nfloat calculateAngle(vec2 p, vec2 c) {\n float a = atan(p.y - c.y, p.x - c.x);\n return a < 0.0 ? a + TWO_PI : a;\n}\n\nfloat angularDiff(float a, float b) {\n float d = abs(a - b);\n return d > PI ? TWO_PI - d : d;\n}\n\nfloat angularFade(float pointAngle, float peakAngle, float fadeAmount) {\n return 1.04 - smoothstep(0.0, fadeAmount, angularDiff(pointAngle, peakAngle));\n}\n\nfloat sdEquilateralTriangle(vec2 p) {\n const float k = 1.7320508;\n p.x = abs(p.x) - 1.0;\n p.y = p.y + 1.0 / k;\n if (p.x + k * p.y > 0.0) {\n p = vec2(p.x - k * p.y, -k * p.x - p.y) / 2.0;\n }\n p.x -= clamp(p.x, -2.0, 0.0);\n return -length(p) * sign(p.y);\n}\n\nvec3 dodge(vec3 src, vec3 dst) {\n return vec3(\n src.x >= 1.0 ? 1.0 : min(1.0, dst.x / max(0.001, 1.0 - src.x)),\n src.y >= 1.0 ? 1.0 : min(1.0, dst.y / max(0.001, 1.0 - src.y)),\n src.z >= 1.0 ? 1.0 : min(1.0, dst.z / max(0.001, 1.0 - src.z))\n );\n}\n\nfloat easeExpoIn(float t) {\n return t <= 0.0 ? 0.0 : pow(2.0, 10.0 * (t - 1.0));\n}\n\nvec2 rot2(vec2 v, float a) {\n float c = cos(a);\n float s = sin(a);\n return vec2(v.x * c - v.y * s, v.x * s + v.y * c);\n}\n\nvec3 beamAt(vec2 uv) {\n float aspect = uResolution.x / uResolution.y;\n vec2 center = uBeamCenter;\n\n vec2 u2 = vec2(uv.x * aspect, uv.y);\n vec2 c2 = vec2(center.x * aspect, center.y);\n\n float ringRadius = uBeamRadius;\n vec2 delta = (u2 - c2) / max(uBeamScale, 0.0001);\n float circleDist = abs(length(delta) - ringRadius);\n float squareDist = abs(max(abs(delta.x), abs(delta.y)) - ringRadius);\n float diamondDist = abs(abs(delta.x) + abs(delta.y) - ringRadius);\n float ovalDist =\n abs(length(vec2(delta.x / 1.5, delta.y)) - ringRadius);\n float triangleDist =\n abs(sdEquilateralTriangle(delta / max(ringRadius, 0.0001))) * ringRadius;\n\n float ringDist = circleDist;\n if (uPathShape == 1) {\n ringDist = squareDist;\n } else if (uPathShape == 2) {\n ringDist = diamondDist;\n } else if (uPathShape == 3) {\n ringDist = triangleDist;\n } else if (uPathShape == 4) {\n ringDist = ovalDist;\n }\n\n float b = 0.25 / (1.0 - smoothstep(0.2, 0.002, ringDist + 0.02));\n float ang = fract(0.19 + uTime * uBeamSpeed * uBeamDirection) * TWO_PI;\n b *= angularFade(calculateAngle(u2, c2), ang, PI * 0.5);\n\n vec3 col = b * pow(max(0.0, 1.0 - ringDist), 3.0) * uBeamColor;\n col = tanh(clamp(col, -40.0, 40.0));\n col += (randomFibo(gl_FragCoord.xy) - 0.5) / 255.0;\n\n return col;\n}\n\nvoid main() {\n vec2 uv = vTextureCoord;\n\n float ang = uPathAngle;\n vec2 pos = uPathPos;\n vec2 off = uv - pos;\n vec2 ro = rot2(off, -ang);\n vec2 so = ro;\n\n if (ro.x > 0.0) {\n float e = easeExpoIn(ro.x);\n so.y = ro.y / (1.0 + 4.0 * e * e);\n }\n\n vec2 st = clamp(pos + rot2(so, ang), 0.0, 1.0);\n\n vec3 beam = beamAt(st);\n vec4 img = texture(uTexture, st);\n vec3 outColor = mix(beam, dodge(img.rgb, beam), img.a);\n\n fragColor = vec4(outColor, 1.0);\n}\n";
5144
+
5145
+ // src/shaders/dither-godray-beam-composite/vertex.glsl
5146
+ var vertex_default14 = "out vec2 vTextureCoord;\n\nvoid main() {\n vTextureCoord = uv;\n gl_Position = vec4(position, 1.0);\n}\n";
5147
+
5148
+ // src/components/DitherStreamBeamCompositePass.tsx
5149
+ import { jsx as jsx24 } from "react/jsx-runtime";
5150
+ function DitherStreamBeamCompositePass({
5151
+ inputTexture = null,
5152
+ beamSpeed = 0.1,
5153
+ beamDirection = "counterclockwise",
5154
+ beamColor = [0.667, 0.686, 0.941],
5155
+ beamCenter = [0.5, 0.95],
5156
+ beamRadius = 0.6,
5157
+ beamScale = 1,
5158
+ pathShape = "circle",
5159
+ pathPos = [0.5009, 1.0473],
5160
+ pathAngle = (0.999 - 0.25) * -6.28318531,
5161
+ target = null,
5162
+ clear = true,
5163
+ enabled = true,
5164
+ priority = 0
5165
+ }) {
5166
+ const fallbackTexture = useMemo16(() => createFallbackTexture(), []);
5167
+ const uniforms = useMemo16(
5168
+ () => ({
5169
+ uTexture: { value: inputTexture ?? fallbackTexture },
5170
+ uTime: { value: 0 },
5171
+ uResolution: { value: new THREE21.Vector2(1, 1) },
5172
+ uBeamSpeed: { value: beamSpeed },
5173
+ uBeamDirection: { value: beamDirection === "clockwise" ? -1 : 1 },
5174
+ uBeamColor: {
5175
+ value: new THREE21.Color(beamColor[0], beamColor[1], beamColor[2])
5176
+ },
5177
+ uBeamCenter: { value: new THREE21.Vector2(beamCenter[0], beamCenter[1]) },
5178
+ uBeamRadius: { value: beamRadius },
5179
+ uBeamScale: { value: beamScale },
5180
+ uPathShape: {
5181
+ value: pathShape === "square" ? 1 : pathShape === "diamond" ? 2 : pathShape === "triangle" ? 3 : pathShape === "oval" ? 4 : 0
5182
+ },
5183
+ uPathPos: { value: new THREE21.Vector2(pathPos[0], pathPos[1]) },
5184
+ uPathAngle: { value: pathAngle }
5185
+ }),
5186
+ // eslint-disable-next-line react-hooks/exhaustive-deps
5187
+ []
5188
+ );
5189
+ uniforms.uTexture.value = inputTexture ?? fallbackTexture;
5190
+ uniforms.uBeamSpeed.value = beamSpeed;
5191
+ uniforms.uBeamDirection.value = beamDirection === "clockwise" ? -1 : 1;
5192
+ uniforms.uBeamColor.value.setRGB(
5193
+ beamColor[0],
5194
+ beamColor[1],
5195
+ beamColor[2]
5196
+ );
5197
+ uniforms.uBeamCenter.value.set(
5198
+ beamCenter[0],
5199
+ beamCenter[1]
5200
+ );
5201
+ uniforms.uBeamRadius.value = beamRadius;
5202
+ uniforms.uBeamScale.value = beamScale;
5203
+ uniforms.uPathShape.value = pathShape === "square" ? 1 : pathShape === "diamond" ? 2 : pathShape === "triangle" ? 3 : pathShape === "oval" ? 4 : 0;
5204
+ uniforms.uPathPos.value.set(pathPos[0], pathPos[1]);
5205
+ uniforms.uPathAngle.value = pathAngle;
5206
+ return /* @__PURE__ */ jsx24(
5207
+ ShaderPass,
5208
+ {
5209
+ vertexShader: vertex_default14,
5210
+ fragmentShader: fragment_default15,
5211
+ uniforms,
5212
+ inputTexture: inputTexture ?? fallbackTexture,
5213
+ target,
5214
+ clear,
5215
+ enabled,
5216
+ priority,
5217
+ timeUniform: "uTime",
5218
+ resolutionUniform: "uResolution",
5219
+ blending: THREE21.NoBlending
5220
+ }
5221
+ );
5222
+ }
5223
+
5224
+ // src/components/DitherStreamDitherPass.tsx
5225
+ import { useMemo as useMemo17 } from "react";
5226
+ import * as THREE22 from "three";
5227
+
5228
+ // src/shaders/dither-godray-dither/fragment.glsl
5229
+ var fragment_default16 = "precision highp float;\n\nin vec2 vTextureCoord;\n\nuniform sampler2D uTexture;\nuniform vec2 uResolution;\n\nout vec4 fragColor;\n\nvoid main() {\n vec2 uv = vTextureCoord;\n\n float ar = uResolution.x / uResolution.y;\n float ac = mix(ar, 1.0 / ar, 0.5);\n\n float gs = 0.005;\n float bg = 1.0 / gs;\n vec2 cellSize = vec2(1.0 / (bg * ar), 1.0 / bg) * ac;\n\n vec2 pos = vec2(0.5);\n vec2 off = uv - pos;\n vec2 cell = floor(off / cellSize);\n vec2 center = (cell + 0.5) * cellSize;\n vec2 pixelUv = center + pos;\n\n vec4 c = texture(uTexture, pixelUv);\n float lum = dot(c.rgb, vec3(0.2126, 0.7152, 0.0722));\n float gm = pow(mix(0.2, 2.2, 0.3), 2.2);\n\n vec2 local = mod(uv - pos, cellSize) / cellSize;\n vec2 ct = local * 2.0 - 1.0;\n float d = length(ct);\n\n float ns = 16.0;\n float si = clamp(floor(lum * ns * gm), 0.0, ns - 1.0);\n float rad = si / ns;\n float alpha = smoothstep(rad + 0.08, rad - 0.08, d);\n\n vec3 tint = (c.rgb - si * 0.04) * 1.4;\n fragColor = vec4(mix(vec3(0.0), tint, alpha), 1.0);\n}\n";
5230
+
5231
+ // src/shaders/dither-godray-dither/vertex.glsl
5232
+ var vertex_default15 = "out vec2 vTextureCoord;\n\nvoid main() {\n vTextureCoord = uv;\n gl_Position = vec4(position, 1.0);\n}\n";
5233
+
5234
+ // src/components/DitherStreamDitherPass.tsx
5235
+ import { jsx as jsx25 } from "react/jsx-runtime";
5236
+ function DitherStreamDitherPass({
5237
+ inputTexture = null,
5238
+ target = null,
5239
+ clear = true,
5240
+ enabled = true,
5241
+ priority = 0
5242
+ }) {
5243
+ const fallbackTexture = useMemo17(() => createFallbackTexture(), []);
5244
+ const uniforms = useMemo17(
5245
+ () => ({
5246
+ uTexture: { value: inputTexture ?? fallbackTexture },
5247
+ uResolution: { value: new THREE22.Vector2(1, 1) }
5248
+ }),
5249
+ // eslint-disable-next-line react-hooks/exhaustive-deps
5250
+ []
5251
+ );
5252
+ uniforms.uTexture.value = inputTexture ?? fallbackTexture;
5253
+ return /* @__PURE__ */ jsx25(
5254
+ ShaderPass,
5255
+ {
5256
+ vertexShader: vertex_default15,
5257
+ fragmentShader: fragment_default16,
5258
+ uniforms,
5259
+ inputTexture: inputTexture ?? fallbackTexture,
5260
+ target,
5261
+ clear,
5262
+ enabled,
5263
+ priority,
5264
+ resolutionUniform: "uResolution",
5265
+ blending: THREE22.NoBlending
5266
+ }
5267
+ );
5268
+ }
5269
+
5270
+ // src/components/DitherStreamGodRaysPass.tsx
5271
+ import { useEffect as useEffect17, useMemo as useMemo18 } from "react";
5272
+ import * as THREE23 from "three";
5273
+
5274
+ // src/shaders/dither-godray-extract/fragment.glsl
5275
+ var fragment_default17 = "precision highp float;\n\nin vec2 vTextureCoord;\nuniform sampler2D uTexture;\nout vec4 fragColor;\n\nvoid main() {\n vec4 c = texture(uTexture, vTextureCoord);\n float l = dot(c.rgb, vec3(0.299, 0.587, 0.114));\n fragColor = c * smoothstep(-0.1, 0.0, l);\n}\n";
5276
+
5277
+ // src/shaders/dither-godray-extract/vertex.glsl
5278
+ var vertex_default16 = "out vec2 vTextureCoord;\n\nvoid main() {\n vTextureCoord = uv;\n gl_Position = vec4(position, 1.0);\n}\n";
5279
+
5280
+ // src/shaders/dither-godray-final/fragment.glsl
5281
+ var fragment_default18 = "precision highp float;\n\nin vec2 vTextureCoord;\nuniform sampler2D uScene;\nuniform sampler2D uGR;\nuniform float uGodrayIntensity;\nout vec4 fragColor;\n\nvoid main() {\n vec3 bg = texture(uScene, vTextureCoord).rgb;\n vec3 gr = texture(uGR, vTextureCoord).rgb;\n fragColor = vec4(bg + gr * uGodrayIntensity, 1.0);\n}\n";
5282
+
5283
+ // src/shaders/dither-godray-final/vertex.glsl
5284
+ var vertex_default17 = "out vec2 vTextureCoord;\n\nvoid main() {\n vTextureCoord = uv;\n gl_Position = vec4(position, 1.0);\n}\n";
5285
+
5286
+ // src/shaders/dither-godray-march/fragment.glsl
5287
+ var fragment_default19 = "precision highp float;\n\nin vec2 vTextureCoord;\nuniform sampler2D uTexture;\nout vec4 fragColor;\n\nvoid main() {\n vec3 col = vec3(0.0);\n vec2 pos = vec2(0.5);\n vec2 dir = (pos - vTextureCoord) * 0.02;\n float w = 1.0;\n vec2 tc = vTextureCoord;\n\n for (int i = 0; i < 16; i++) {\n col += texture(uTexture, tc).rgb * w;\n w *= 0.94;\n tc += dir;\n }\n\n fragColor = vec4(col / 16.0, length(tc - vTextureCoord));\n}\n";
5288
+
5289
+ // src/shaders/dither-godray-march/vertex.glsl
5290
+ var vertex_default18 = "out vec2 vTextureCoord;\n\nvoid main() {\n vTextureCoord = uv;\n gl_Position = vec4(position, 1.0);\n}\n";
5291
+
5292
+ // src/components/DitherStreamGodRaysPass.tsx
5293
+ import { Fragment as Fragment4, jsx as jsx26, jsxs as jsxs3 } from "react/jsx-runtime";
5294
+ var TARGET_OPTIONS = {
5295
+ depthBuffer: false,
5296
+ stencilBuffer: false
5297
+ };
5298
+ function DitherStreamGodRaysPass({
5299
+ inputTexture = null,
5300
+ intensity = 2.9,
5301
+ target = null,
5302
+ enabled = true,
5303
+ priority = 0
5304
+ }) {
5305
+ const sharedScene = useSceneContext();
5306
+ const size = sharedScene?.size ?? { width: 1, height: 1 };
5307
+ const fallbackTexture = useMemo18(() => createFallbackTexture(), []);
5308
+ const extractTarget = useMemo18(
5309
+ () => new THREE23.WebGLRenderTarget(1, 1, TARGET_OPTIONS),
5310
+ []
5311
+ );
5312
+ const marchTarget = useMemo18(
5313
+ () => new THREE23.WebGLRenderTarget(1, 1, TARGET_OPTIONS),
5314
+ []
5315
+ );
5316
+ useEffect17(() => {
5317
+ return () => {
5318
+ extractTarget.dispose();
5319
+ marchTarget.dispose();
5320
+ };
5321
+ }, [extractTarget, marchTarget]);
5322
+ useEffect17(() => {
5323
+ if (size.width <= 1 || size.height <= 1) return;
5324
+ extractTarget.setSize(size.width, size.height);
5325
+ marchTarget.setSize(
5326
+ Math.max(1, Math.floor(size.width / 4)),
5327
+ Math.max(1, Math.floor(size.height / 4))
5328
+ );
5329
+ }, [extractTarget, marchTarget, size.height, size.width]);
5330
+ const extractUniforms = useMemo18(
5331
+ () => ({
5332
+ uTexture: { value: inputTexture ?? fallbackTexture }
5333
+ }),
5334
+ // eslint-disable-next-line react-hooks/exhaustive-deps
5335
+ []
5336
+ );
5337
+ const marchUniforms = useMemo18(
5338
+ () => ({
5339
+ uTexture: { value: extractTarget.texture }
5340
+ }),
5341
+ // eslint-disable-next-line react-hooks/exhaustive-deps
5342
+ []
5343
+ );
5344
+ const finalUniforms = useMemo18(
5345
+ () => ({
5346
+ uScene: { value: inputTexture ?? fallbackTexture },
5347
+ uGR: { value: marchTarget.texture },
5348
+ uGodrayIntensity: { value: intensity }
5349
+ }),
5350
+ // eslint-disable-next-line react-hooks/exhaustive-deps
5351
+ []
5352
+ );
5353
+ extractUniforms.uTexture.value = inputTexture ?? fallbackTexture;
5354
+ marchUniforms.uTexture.value = extractTarget.texture;
5355
+ finalUniforms.uScene.value = inputTexture ?? fallbackTexture;
5356
+ finalUniforms.uGR.value = marchTarget.texture;
5357
+ finalUniforms.uGodrayIntensity.value = intensity;
5358
+ return /* @__PURE__ */ jsxs3(Fragment4, { children: [
5359
+ /* @__PURE__ */ jsx26(
5360
+ ShaderPass,
5361
+ {
5362
+ vertexShader: vertex_default16,
5363
+ fragmentShader: fragment_default17,
5364
+ uniforms: extractUniforms,
5365
+ inputTexture: inputTexture ?? fallbackTexture,
5366
+ target: extractTarget,
5367
+ clear: true,
5368
+ enabled,
5369
+ priority,
5370
+ blending: THREE23.NoBlending
5371
+ }
5372
+ ),
5373
+ /* @__PURE__ */ jsx26(
5374
+ ShaderPass,
5375
+ {
5376
+ vertexShader: vertex_default18,
5377
+ fragmentShader: fragment_default19,
5378
+ uniforms: marchUniforms,
5379
+ inputTexture: extractTarget.texture,
5380
+ target: marchTarget,
5381
+ clear: true,
5382
+ enabled,
5383
+ priority: priority + 1,
5384
+ blending: THREE23.NoBlending
5385
+ }
5386
+ ),
5387
+ /* @__PURE__ */ jsx26(
5388
+ ShaderPass,
5389
+ {
5390
+ vertexShader: vertex_default17,
5391
+ fragmentShader: fragment_default18,
5392
+ uniforms: finalUniforms,
5393
+ inputTexture: inputTexture ?? fallbackTexture,
5394
+ target,
5395
+ clear: true,
5396
+ enabled,
5397
+ priority: priority + 2,
5398
+ blending: THREE23.NoBlending
5399
+ }
5400
+ )
5401
+ ] });
5402
+ }
5403
+
5404
+ // src/components/DitherStreamProjectionPass.tsx
5405
+ import { useEffect as useEffect18, useMemo as useMemo19, useState as useState4 } from "react";
5406
+ import * as THREE24 from "three";
5407
+
5408
+ // src/shaders/dither-godray-projection/fragment.glsl
5409
+ var fragment_default20 = "precision highp float;\n\nin vec2 vTextureCoord;\n\nuniform sampler2D uTex;\nuniform float uTime;\nuniform float uProjectionSpeed;\n\nout vec4 fragColor;\n\nconst float PI = 3.14159265;\n\nvec3 ray(vec2 uv, vec2 m, float a) {\n vec2 s = (uv - 0.5) * 2.0;\n s.x *= a;\n s.y *= -1.0;\n\n float f = mix(radians(20.0), radians(120.0), 0.38);\n vec3 r = normalize(vec3(s * tan(f / 2.0), -1.0));\n\n float rx = (m.y - 0.5) * PI;\n float ry = (m.x - 0.5) * PI * 2.0;\n mat3 rY = mat3(cos(ry), 0.0, -sin(ry), 0.0, 1.0, 0.0, sin(ry), 0.0, cos(ry));\n mat3 rX = mat3(1.0, 0.0, 0.0, 0.0, cos(rx), sin(rx), 0.0, -sin(rx), cos(rx));\n\n return normalize(rX * rY * r);\n}\n\nvec2 directionToUv(vec3 d) {\n return vec2(atan(d.z, d.x) / (2.0 * PI) + 0.75, acos(clamp(d.y, -1.0, 1.0)) / PI);\n}\n\nvec4 sampleRepeat(vec2 uv) {\n vec2 f = vec2(uv.x, fract(uv.y));\n vec4 c = texture(uTex, f);\n\n float blendWidth = 0.1;\n float blendFactor = 0.0;\n if (f.y < blendWidth) {\n blendFactor = 1.0 - f.y / blendWidth;\n } else if (f.y > 1.0 - blendWidth) {\n blendFactor = (f.y - (1.0 - blendWidth)) / blendWidth;\n }\n\n if (blendFactor > 0.0) {\n blendFactor = smoothstep(0.0, 1.0, blendFactor);\n vec2 opposite = vec2(f.x, f.y > 0.5 ? f.y - 0.5 : f.y + 0.5);\n c = mix(c, texture(uTex, opposite), blendFactor);\n }\n\n return c;\n}\n\nvoid main() {\n vec2 uv = vTextureCoord;\n\n vec3 rd = ray(uv, vec2(0.5, 0.5056), 2.0);\n vec2 s = directionToUv(rd);\n\n float f = mix(radians(20.0), radians(120.0), 0.38);\n s = (s - 0.5) * (2.0 / tan(f / 2.0)) + 0.5;\n s += vec2(0.0, 0.02) * uTime * uProjectionSpeed;\n\n vec4 c = sampleRepeat(s);\n c.rgb = clamp(c.rgb - 0.14, 0.0, 1.0);\n\n float l = dot(c.rgb, vec3(0.299, 0.587, 0.114));\n c.rgb = mix(vec3(l), c.rgb, 0.79);\n c.rgb = 1.22 * (c.rgb - 0.5) + 0.5;\n c.rgb = clamp(c.rgb, 0.0, 1.0);\n\n fragColor = c;\n}\n";
5410
+
5411
+ // src/shaders/dither-godray-projection/vertex.glsl
5412
+ var vertex_default19 = "out vec2 vTextureCoord;\n\nvoid main() {\n vTextureCoord = uv;\n gl_Position = vec4(position, 1.0);\n}\n";
5413
+
5414
+ // src/components/DitherStreamProjectionPass.tsx
5415
+ import { jsx as jsx27 } from "react/jsx-runtime";
5416
+ var DEFAULT_IMAGE_SRC = "https://firebasestorage.googleapis.com/v0/b/unicorn-studio.appspot.com/o/Zz28X5RDkvcGGVYLr9X6QdTIhxy1%2FUntitled%20design%20-%202025-10-14T141250.707.webp?alt=media&token=bfcd11a8-6529-41a6-a592-d78147e93840";
5417
+ function DitherStreamProjectionPass({
5418
+ imageTextureSrc = DEFAULT_IMAGE_SRC,
5419
+ projectionSpeed = 0.05,
5420
+ target = null,
5421
+ clear = true,
5422
+ enabled = true,
5423
+ priority = 0
5424
+ }) {
5425
+ const [imageTexture, setImageTexture] = useState4(null);
5426
+ const fallbackTexture = useMemo19(() => createFallbackTexture(), []);
5427
+ useEffect18(() => {
5428
+ let active = true;
5429
+ const loader = new THREE24.TextureLoader();
5430
+ loader.load(
5431
+ imageTextureSrc,
5432
+ (texture) => {
5433
+ if (!active) {
5434
+ texture.dispose();
5435
+ return;
5436
+ }
5437
+ texture.wrapS = THREE24.RepeatWrapping;
5438
+ texture.wrapT = THREE24.RepeatWrapping;
5439
+ texture.minFilter = THREE24.LinearFilter;
5440
+ texture.magFilter = THREE24.LinearFilter;
5441
+ texture.generateMipmaps = true;
5442
+ texture.colorSpace = THREE24.NoColorSpace;
5443
+ texture.needsUpdate = true;
5444
+ setImageTexture(texture);
5445
+ },
5446
+ void 0,
5447
+ () => {
5448
+ if (!active) return;
5449
+ setImageTexture(fallbackTexture);
5450
+ }
5451
+ );
5452
+ return () => {
5453
+ active = false;
5454
+ };
5455
+ }, [fallbackTexture, imageTextureSrc]);
5456
+ const uniforms = useMemo19(
5457
+ () => ({
5458
+ uTex: { value: imageTexture ?? fallbackTexture },
5459
+ uTime: { value: 0 },
5460
+ uProjectionSpeed: { value: projectionSpeed }
5461
+ }),
5462
+ // eslint-disable-next-line react-hooks/exhaustive-deps
5463
+ []
5464
+ );
5465
+ uniforms.uTex.value = imageTexture ?? fallbackTexture;
5466
+ uniforms.uProjectionSpeed.value = projectionSpeed;
5467
+ return /* @__PURE__ */ jsx27(
5468
+ ShaderPass,
5469
+ {
5470
+ vertexShader: vertex_default19,
5471
+ fragmentShader: fragment_default20,
5472
+ uniforms,
5473
+ target,
5474
+ clear,
5475
+ enabled,
5476
+ priority,
5477
+ timeUniform: "uTime",
5478
+ blending: THREE24.NoBlending
5479
+ }
5480
+ );
5481
+ }
5482
+
5483
+ // src/components/DitherStreamRendererConfig.tsx
5484
+ import { useEffect as useEffect19 } from "react";
5485
+ import * as THREE25 from "three";
5486
+ function DitherStreamRendererConfig() {
5487
+ const sharedScene = useSceneContext();
5488
+ useEffect19(() => {
5489
+ const context = sharedScene?.contextRef.current;
5490
+ if (!context) return;
5491
+ const renderer = context.renderer;
5492
+ const prevOutputColorSpace = renderer.outputColorSpace;
5493
+ const prevToneMapping = renderer.toneMapping;
5494
+ renderer.outputColorSpace = THREE25.LinearSRGBColorSpace;
5495
+ renderer.toneMapping = THREE25.NoToneMapping;
5496
+ return () => {
5497
+ renderer.outputColorSpace = prevOutputColorSpace;
5498
+ renderer.toneMapping = prevToneMapping;
5499
+ };
5500
+ }, [sharedScene]);
5501
+ return null;
5502
+ }
5503
+
5504
+ // src/components/DitherStream.tsx
5505
+ import { jsx as jsx28, jsxs as jsxs4 } from "react/jsx-runtime";
5506
+ function DitherStream({
5507
+ width = "100%",
5508
+ height = "100%",
5509
+ className = "relative h-full w-full",
5510
+ style,
5511
+ imageTextureSrc,
5512
+ projectionSpeed = 0.05,
5513
+ beamSpeed = 0.1,
5514
+ beamDirection = "counterclockwise",
5515
+ beamColor = [0.667, 0.686, 0.941],
5516
+ beamCenter = [0.5, 0.95],
5517
+ beamRadius = 0.6,
5518
+ beamScale = 1,
5519
+ beamPathShape = "circle",
5520
+ pathPos = [0.5009, 1.0473],
5521
+ pathAngle = (0.999 - 0.25) * -6.28318531,
5522
+ godrayIntensity = 2.9
5523
+ }) {
5524
+ const resolveColor = (color) => {
5525
+ if (Array.isArray(color)) return color;
5526
+ const normalized = color.replace("#", "");
5527
+ if (normalized.length !== 6)
5528
+ return [0.667, 0.686, 0.941];
5529
+ return [
5530
+ parseInt(normalized.slice(0, 2), 16) / 255,
5531
+ parseInt(normalized.slice(2, 4), 16) / 255,
5532
+ parseInt(normalized.slice(4, 6), 16) / 255
5533
+ ];
5534
+ };
5535
+ const resolvedBeamColor = resolveColor(beamColor);
5536
+ return /* @__PURE__ */ jsxs4(
5537
+ SceneProvider,
5538
+ {
5539
+ width,
5540
+ height,
5541
+ className,
5542
+ style,
5543
+ manualRender: true,
5544
+ children: [
5545
+ /* @__PURE__ */ jsx28(DitherStreamRendererConfig, {}),
5546
+ /* @__PURE__ */ jsx28(
5547
+ RenderPipeline,
5548
+ {
5549
+ passes: [
5550
+ {
5551
+ component: DitherStreamProjectionPass,
5552
+ props: { imageTextureSrc, projectionSpeed }
5553
+ },
5554
+ {
5555
+ component: DitherStreamBeamCompositePass,
5556
+ props: {
5557
+ beamSpeed,
5558
+ beamDirection,
5559
+ beamColor: resolvedBeamColor,
5560
+ beamCenter,
5561
+ beamRadius,
5562
+ beamScale,
5563
+ pathShape: beamPathShape,
5564
+ pathPos,
5565
+ pathAngle
5566
+ }
5567
+ },
5568
+ {
5569
+ component: DitherStreamDitherPass
5570
+ },
5571
+ {
5572
+ component: DitherStreamGodRaysPass,
5573
+ props: { intensity: godrayIntensity }
5574
+ }
5575
+ ]
5576
+ }
5577
+ )
5578
+ ]
5579
+ }
5580
+ );
5581
+ }
5118
5582
  export {
5119
5583
  AnimatedDrawingSVG,
5120
5584
  DitherPulseRing,
5585
+ DitherStream,
5121
5586
  EFECTO_ASCII_COMPONENT_DEFAULTS,
5122
5587
  EFECTO_ASCII_POST_PROCESSING_DEFAULTS,
5123
5588
  Efecto,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toriistudio/shader-ui",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "description": "Shader components",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",