@toriistudio/shader-ui 0.0.7 → 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.mjs CHANGED
@@ -3533,11 +3533,8 @@ import {
3533
3533
  } from "react";
3534
3534
  import * as THREE8 from "three";
3535
3535
 
3536
- // src/hooks/SceneProvider.tsx
3537
- import {
3538
- createContext,
3539
- useContext
3540
- } from "react";
3536
+ // src/context/SceneProvider.tsx
3537
+ import { createContext, useContext } from "react";
3541
3538
  import { jsx as jsx11 } from "react/jsx-runtime";
3542
3539
  var SceneProviderContext = createContext(null);
3543
3540
  function SceneProvider({
@@ -3732,7 +3729,15 @@ function ShaderPass({
3732
3729
  context.renderer.setRenderTarget(prevTarget);
3733
3730
  context.renderer.autoClear = prevAutoClear;
3734
3731
  },
3735
- [clear, clearColor, enabled, resolutionUniform, target, timeUniform, uniforms]
3732
+ [
3733
+ clear,
3734
+ clearColor,
3735
+ enabled,
3736
+ resolutionUniform,
3737
+ target,
3738
+ timeUniform,
3739
+ uniforms
3740
+ ]
3736
3741
  );
3737
3742
  const sharedContextRef = sharedScene?.contextRef;
3738
3743
  const shouldCreateOwnScene = !sharedContextRef;
@@ -3762,13 +3767,10 @@ function ShaderPass({
3762
3767
  renderOnce();
3763
3768
  return;
3764
3769
  }
3765
- const unregister = register(
3766
- (ctx, delta, elapsed) => {
3767
- void elapsed;
3768
- handleRender(ctx, delta);
3769
- },
3770
- priority
3771
- );
3770
+ const unregister = register((ctx, delta, elapsed) => {
3771
+ void elapsed;
3772
+ handleRender(ctx, delta);
3773
+ }, priority);
3772
3774
  return () => {
3773
3775
  unregister?.();
3774
3776
  };
@@ -4221,82 +4223,21 @@ function ExpandingRingPass({
4221
4223
  }
4222
4224
 
4223
4225
  // src/components/RenderPipeline.tsx
4224
- import { useEffect as useEffect14, useMemo as useMemo12 } from "react";
4226
+ import { Fragment, useEffect as useEffect14, useMemo as useMemo12 } from "react";
4225
4227
  import * as THREE16 from "three";
4226
4228
 
4227
- // src/components/CombineShaderPass.tsx
4228
- import { useMemo as useMemo11 } from "react";
4229
- import * as THREE14 from "three";
4230
- import { jsx as jsx18 } from "react/jsx-runtime";
4231
- var VERT = `
4232
- out vec2 vUv;
4233
-
4234
- void main() {
4235
- vUv = uv;
4236
- gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
4237
- }
4238
- `;
4239
- var FRAG = `
4240
- precision highp float;
4241
-
4242
- in vec2 vUv;
4243
-
4244
- uniform sampler2D uTextureA;
4245
- uniform sampler2D uTextureB;
4246
-
4247
- out vec4 fragColor;
4248
-
4249
- void main() {
4250
- vec4 a = texture(uTextureA, vUv);
4251
- vec4 b = texture(uTextureB, vUv);
4252
- vec3 rgb = a.rgb + b.rgb;
4253
- float alpha = max(a.a, b.a);
4254
- fragColor = vec4(rgb, alpha);
4255
- }
4256
- `;
4257
- function CombineShaderPass({
4258
- inputA,
4259
- inputB,
4260
- target = null,
4261
- clear = true,
4262
- priority = 0
4263
- }) {
4264
- const uniforms = useMemo11(
4265
- () => ({
4266
- uTextureA: { value: inputA },
4267
- uTextureB: { value: inputB }
4268
- }),
4269
- // eslint-disable-next-line react-hooks/exhaustive-deps
4270
- []
4271
- );
4272
- uniforms.uTextureA.value = inputA;
4273
- uniforms.uTextureB.value = inputB;
4274
- return /* @__PURE__ */ jsx18(
4275
- ShaderPass,
4276
- {
4277
- vertexShader: VERT,
4278
- fragmentShader: FRAG,
4279
- uniforms,
4280
- target,
4281
- clear,
4282
- priority,
4283
- blending: THREE14.NoBlending
4284
- }
4285
- );
4286
- }
4287
-
4288
4229
  // src/components/TargetPreview.tsx
4289
4230
  import {
4290
4231
  useCallback as useCallback8,
4291
4232
  useEffect as useEffect13,
4292
4233
  useRef as useRef11
4293
4234
  } from "react";
4294
- import * as THREE15 from "three";
4295
- import { jsx as jsx19 } from "react/jsx-runtime";
4235
+ import * as THREE14 from "three";
4236
+ import { jsx as jsx18 } from "react/jsx-runtime";
4296
4237
  function TargetPreview({
4297
4238
  target,
4298
4239
  renderOrder = 0,
4299
- blending = THREE15.NoBlending,
4240
+ blending = THREE14.NoBlending,
4300
4241
  transparent = true,
4301
4242
  toneMapped = false,
4302
4243
  className,
@@ -4309,11 +4250,11 @@ function TargetPreview({
4309
4250
  const sharedScene = useSceneContext();
4310
4251
  const sharedReady = sharedScene?.ready ?? true;
4311
4252
  const handleCreate = useCallback8(() => {
4312
- const scene = new THREE15.Scene();
4313
- const camera = new THREE15.OrthographicCamera(-1, 1, 1, -1, 0, 1);
4253
+ const scene = new THREE14.Scene();
4254
+ const camera = new THREE14.OrthographicCamera(-1, 1, 1, -1, 0, 1);
4314
4255
  camera.position.z = 1;
4315
- const geometry = new THREE15.PlaneGeometry(2, 2);
4316
- const material = new THREE15.MeshBasicMaterial({
4256
+ const geometry = new THREE14.PlaneGeometry(2, 2);
4257
+ const material = new THREE14.MeshBasicMaterial({
4317
4258
  map: target.texture,
4318
4259
  toneMapped,
4319
4260
  transparent,
@@ -4321,7 +4262,7 @@ function TargetPreview({
4321
4262
  depthWrite: false,
4322
4263
  depthTest: false
4323
4264
  });
4324
- const mesh = new THREE15.Mesh(geometry, material);
4265
+ const mesh = new THREE14.Mesh(geometry, material);
4325
4266
  mesh.renderOrder = renderOrder;
4326
4267
  scene.add(mesh);
4327
4268
  assetsRef.current = { scene, camera, mesh, geometry, material };
@@ -4360,12 +4301,9 @@ function TargetPreview({
4360
4301
  handleRender(context);
4361
4302
  return;
4362
4303
  }
4363
- const unregister = register(
4364
- (ctx) => {
4365
- handleRender(ctx);
4366
- },
4367
- renderOrder
4368
- );
4304
+ const unregister = register((ctx) => {
4305
+ handleRender(ctx);
4306
+ }, renderOrder);
4369
4307
  return () => {
4370
4308
  unregister?.();
4371
4309
  };
@@ -4387,7 +4325,7 @@ function TargetPreview({
4387
4325
  if (!shouldCreateOwnScene) {
4388
4326
  return null;
4389
4327
  }
4390
- return /* @__PURE__ */ jsx19(
4328
+ return /* @__PURE__ */ jsx18(
4391
4329
  "div",
4392
4330
  {
4393
4331
  ref: containerRef,
@@ -4402,30 +4340,260 @@ function TargetPreview({
4402
4340
  );
4403
4341
  }
4404
4342
 
4343
+ // src/components/CombineShaderPass.tsx
4344
+ import { useMemo as useMemo11 } from "react";
4345
+ import * as THREE15 from "three";
4346
+ import { jsx as jsx19 } from "react/jsx-runtime";
4347
+ var COMBINE_MODE = {
4348
+ add: 0,
4349
+ screen: 1,
4350
+ multiply: 2,
4351
+ overlay: 3,
4352
+ max: 4,
4353
+ min: 5,
4354
+ difference: 6,
4355
+ alphaOver: 7,
4356
+ premultipliedOver: 8,
4357
+ lerp: 9,
4358
+ mask: 10
4359
+ };
4360
+ var TONEMAP = {
4361
+ none: 0,
4362
+ reinhard: 1,
4363
+ aces: 2
4364
+ };
4365
+ var VERT = `
4366
+ out vec2 vUv;
4367
+
4368
+ void main() {
4369
+ vUv = uv;
4370
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
4371
+ }
4372
+ `;
4373
+ var FRAG = `
4374
+ precision highp float;
4375
+
4376
+ in vec2 vUv;
4377
+
4378
+ uniform sampler2D uTextureA;
4379
+ uniform sampler2D uTextureB;
4380
+ uniform sampler2D uMaskTexture;
4381
+
4382
+ uniform int uMode;
4383
+ uniform float uOpacityA;
4384
+ uniform float uOpacityB;
4385
+ uniform float uMix;
4386
+ uniform int uMaskChannel;
4387
+ uniform float uMaskThreshold;
4388
+ uniform bool uInvertMask;
4389
+ uniform bool uClampOutput;
4390
+ uniform int uTonemap;
4391
+ uniform bool uHasMask;
4392
+
4393
+ out vec4 fragColor;
4394
+
4395
+ float overlayChannel(float base, float blend) {
4396
+ return base < 0.5
4397
+ ? (2.0 * base * blend)
4398
+ : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend));
4399
+ }
4400
+
4401
+ vec3 overlayBlend(vec3 base, vec3 blend) {
4402
+ return vec3(
4403
+ overlayChannel(base.r, blend.r),
4404
+ overlayChannel(base.g, blend.g),
4405
+ overlayChannel(base.b, blend.b)
4406
+ );
4407
+ }
4408
+
4409
+ float sampleMask(vec2 uv) {
4410
+ vec4 maskSample = texture(uMaskTexture, uv);
4411
+ float m = maskSample.r;
4412
+ if (uMaskChannel == 1) {
4413
+ m = maskSample.g;
4414
+ } else if (uMaskChannel == 2) {
4415
+ m = maskSample.b;
4416
+ } else if (uMaskChannel == 3) {
4417
+ m = maskSample.a;
4418
+ }
4419
+ if (uInvertMask) {
4420
+ m = 1.0 - m;
4421
+ }
4422
+ return m;
4423
+ }
4424
+
4425
+ vec3 tonemapReinhard(vec3 color) {
4426
+ return color / (color + vec3(1.0));
4427
+ }
4428
+
4429
+ vec3 tonemapAces(vec3 color) {
4430
+ // ACES approximation by Krzysztof Narkowicz
4431
+ float a = 2.51;
4432
+ float b = 0.03;
4433
+ float c = 2.43;
4434
+ float d = 0.59;
4435
+ float e = 0.14;
4436
+ return clamp((color * (a * color + b)) / (color * (c * color + d) + e), 0.0, 1.0);
4437
+ }
4438
+
4439
+ void main() {
4440
+ vec4 a = texture(uTextureA, vUv);
4441
+ vec4 b = texture(uTextureB, vUv);
4442
+
4443
+ a.rgb *= uOpacityA;
4444
+ a.a *= uOpacityA;
4445
+ b.rgb *= uOpacityB;
4446
+ b.a *= uOpacityB;
4447
+
4448
+ vec3 rgb = vec3(0.0);
4449
+ float alpha = 0.0;
4450
+
4451
+ if (uMode == 0) {
4452
+ rgb = a.rgb + b.rgb;
4453
+ alpha = max(a.a, b.a);
4454
+ } else if (uMode == 1) {
4455
+ rgb = 1.0 - (1.0 - a.rgb) * (1.0 - b.rgb);
4456
+ alpha = max(a.a, b.a);
4457
+ } else if (uMode == 2) {
4458
+ rgb = a.rgb * b.rgb;
4459
+ alpha = max(a.a, b.a);
4460
+ } else if (uMode == 3) {
4461
+ rgb = overlayBlend(a.rgb, b.rgb);
4462
+ alpha = max(a.a, b.a);
4463
+ } else if (uMode == 4) {
4464
+ rgb = max(a.rgb, b.rgb);
4465
+ alpha = max(a.a, b.a);
4466
+ } else if (uMode == 5) {
4467
+ rgb = min(a.rgb, b.rgb);
4468
+ alpha = max(a.a, b.a);
4469
+ } else if (uMode == 6) {
4470
+ rgb = abs(a.rgb - b.rgb);
4471
+ alpha = max(a.a, b.a);
4472
+ } else if (uMode == 7) {
4473
+ float outA = a.a + b.a * (1.0 - a.a);
4474
+ vec3 premult = a.rgb * a.a + b.rgb * b.a * (1.0 - a.a);
4475
+ vec3 outRgb = outA > 1e-6 ? premult / outA : vec3(0.0);
4476
+ rgb = outRgb;
4477
+ alpha = outA;
4478
+ } else if (uMode == 8) {
4479
+ rgb = a.rgb + b.rgb * (1.0 - a.a);
4480
+ alpha = a.a + b.a * (1.0 - a.a);
4481
+ } else if (uMode == 9) {
4482
+ float t = uHasMask ? sampleMask(vUv) : uMix;
4483
+ rgb = mix(b.rgb, a.rgb, t);
4484
+ alpha = mix(b.a, a.a, t);
4485
+ } else if (uMode == 10) {
4486
+ float t = uHasMask ? sampleMask(vUv) : uMix;
4487
+ bool chooseA = t > uMaskThreshold;
4488
+ rgb = chooseA ? a.rgb : b.rgb;
4489
+ alpha = chooseA ? a.a : b.a;
4490
+ }
4491
+
4492
+ if (uTonemap == 1) {
4493
+ rgb = tonemapReinhard(rgb);
4494
+ } else if (uTonemap == 2) {
4495
+ rgb = tonemapAces(rgb);
4496
+ }
4497
+
4498
+ if (uClampOutput) {
4499
+ rgb = clamp(rgb, 0.0, 1.0);
4500
+ alpha = clamp(alpha, 0.0, 1.0);
4501
+ }
4502
+
4503
+ fragColor = vec4(rgb, alpha);
4504
+ }
4505
+ `;
4506
+ function CombineShaderPass({
4507
+ inputA,
4508
+ inputB,
4509
+ mode = "add",
4510
+ opacityA = 1,
4511
+ opacityB = 1,
4512
+ clampOutput = true,
4513
+ mix = 0.5,
4514
+ maskTexture,
4515
+ maskChannel = 0,
4516
+ maskThreshold = 0.5,
4517
+ invertMask = false,
4518
+ tonemap = "none",
4519
+ target = null,
4520
+ clear = true,
4521
+ priority = 0
4522
+ }) {
4523
+ const uniforms = useMemo11(
4524
+ () => ({
4525
+ uTextureA: { value: inputA },
4526
+ uTextureB: { value: inputB },
4527
+ uMaskTexture: { value: maskTexture ?? inputA },
4528
+ uMode: { value: COMBINE_MODE[mode] },
4529
+ uOpacityA: { value: opacityA },
4530
+ uOpacityB: { value: opacityB },
4531
+ uMix: { value: mix },
4532
+ uMaskChannel: { value: maskChannel },
4533
+ uMaskThreshold: { value: maskThreshold },
4534
+ uInvertMask: { value: invertMask },
4535
+ uClampOutput: { value: clampOutput },
4536
+ uTonemap: { value: TONEMAP[tonemap] },
4537
+ uHasMask: { value: Boolean(maskTexture) }
4538
+ }),
4539
+ // eslint-disable-next-line react-hooks/exhaustive-deps
4540
+ []
4541
+ );
4542
+ uniforms.uTextureA.value = inputA;
4543
+ uniforms.uTextureB.value = inputB;
4544
+ uniforms.uMaskTexture.value = maskTexture ?? inputA;
4545
+ uniforms.uMode.value = COMBINE_MODE[mode];
4546
+ uniforms.uOpacityA.value = opacityA;
4547
+ uniforms.uOpacityB.value = opacityB;
4548
+ uniforms.uMix.value = mix;
4549
+ uniforms.uMaskChannel.value = maskChannel;
4550
+ uniforms.uMaskThreshold.value = maskThreshold;
4551
+ uniforms.uInvertMask.value = invertMask;
4552
+ uniforms.uClampOutput.value = clampOutput;
4553
+ uniforms.uTonemap.value = TONEMAP[tonemap];
4554
+ uniforms.uHasMask.value = Boolean(maskTexture);
4555
+ return /* @__PURE__ */ jsx19(
4556
+ ShaderPass,
4557
+ {
4558
+ vertexShader: VERT,
4559
+ fragmentShader: FRAG,
4560
+ uniforms,
4561
+ target,
4562
+ clear,
4563
+ priority,
4564
+ blending: THREE15.NoBlending
4565
+ }
4566
+ );
4567
+ }
4568
+
4405
4569
  // src/components/RenderPipeline.tsx
4406
- import { Fragment, jsx as jsx20, jsxs } from "react/jsx-runtime";
4570
+ import { Fragment as Fragment2, jsx as jsx20, jsxs } from "react/jsx-runtime";
4407
4571
  var DEFAULT_TARGET_OPTIONS = {
4408
4572
  depthBuffer: false,
4409
4573
  stencilBuffer: false
4410
4574
  };
4411
4575
  function RenderPipeline({
4412
4576
  passes,
4577
+ combine,
4413
4578
  preview = true,
4414
4579
  previewProps
4415
4580
  }) {
4416
4581
  const sharedScene = useSceneContext();
4417
4582
  const size = sharedScene?.size ?? { width: 1, height: 1 };
4583
+ const shouldCombine = Boolean(combine);
4418
4584
  const targets = useMemo12(() => {
4419
4585
  const entries = passes.map((_, index) => ({
4420
4586
  key: `pass_${index}`,
4421
4587
  target: new THREE16.WebGLRenderTarget(1, 1, DEFAULT_TARGET_OPTIONS)
4422
4588
  }));
4423
- entries.push({
4424
- key: "combine",
4425
- target: new THREE16.WebGLRenderTarget(1, 1, DEFAULT_TARGET_OPTIONS)
4426
- });
4589
+ if (shouldCombine) {
4590
+ entries.push({
4591
+ key: "combine",
4592
+ target: new THREE16.WebGLRenderTarget(1, 1, DEFAULT_TARGET_OPTIONS)
4593
+ });
4594
+ }
4427
4595
  return entries;
4428
- }, [passes]);
4596
+ }, [passes, shouldCombine]);
4429
4597
  useEffect14(() => {
4430
4598
  if (size.width <= 1 || size.height <= 1) return;
4431
4599
  targets.forEach(({ target }) => {
@@ -4443,7 +4611,9 @@ function RenderPipeline({
4443
4611
  throw new Error("RenderPipeline requires at least 2 passes.");
4444
4612
  }
4445
4613
  const passTargets = targets.filter((entry) => entry.key.startsWith("pass_"));
4446
- const combineTarget = targets.find((entry) => entry.key === "combine")?.target;
4614
+ const combineTarget = targets.find(
4615
+ (entry) => entry.key === "combine"
4616
+ )?.target;
4447
4617
  const firstEnabledIndex = passes.findIndex((pass) => pass.enabled !== false);
4448
4618
  const secondEnabledIndex = passes.findIndex(
4449
4619
  (pass, index) => index > firstEnabledIndex && pass.enabled !== false
@@ -4453,7 +4623,7 @@ function RenderPipeline({
4453
4623
  if (!passes.length) return null;
4454
4624
  const lastEnabledIndex = [...passes].map((pass, index) => ({ pass, index })).filter(({ pass }) => pass.enabled !== false).map(({ index }) => index).pop();
4455
4625
  if (lastEnabledIndex === void 0) return null;
4456
- if (lastEnabledIndex <= Math.max(firstEnabledIndex, secondEnabledIndex)) {
4626
+ if (shouldCombine && lastEnabledIndex <= Math.max(firstEnabledIndex, secondEnabledIndex)) {
4457
4627
  return combineTarget ?? null;
4458
4628
  }
4459
4629
  return passOutputTarget(lastEnabledIndex);
@@ -4474,7 +4644,7 @@ function RenderPipeline({
4474
4644
  },
4475
4645
  `pass_${index}`
4476
4646
  );
4477
- if (!combineRendered && combineTarget && index === secondEnabledIndex) {
4647
+ if (shouldCombine && !combineRendered && combineTarget && index === secondEnabledIndex) {
4478
4648
  const inputA = passOutputTarget(firstEnabledIndex)?.texture ?? inputTexture;
4479
4649
  const inputB = passOutputTarget(secondEnabledIndex)?.texture ?? inputTexture;
4480
4650
  combineRendered = true;
@@ -4484,18 +4654,19 @@ function RenderPipeline({
4484
4654
  /* @__PURE__ */ jsx20(
4485
4655
  CombineShaderPass,
4486
4656
  {
4657
+ ...combine,
4487
4658
  inputA,
4488
4659
  inputB,
4489
4660
  target: combineTarget
4490
4661
  }
4491
4662
  )
4492
- ] });
4663
+ ] }, `combine_${index}`);
4493
4664
  }
4494
4665
  previousOutput = target;
4495
4666
  return element;
4496
4667
  });
4497
4668
  const outputTarget = getOutputTarget();
4498
- return /* @__PURE__ */ jsxs(Fragment, { children: [
4669
+ return /* @__PURE__ */ jsxs(Fragment2, { children: [
4499
4670
  renderedPasses,
4500
4671
  preview && outputTarget && /* @__PURE__ */ jsx20(
4501
4672
  TargetPreview,
@@ -4567,20 +4738,23 @@ function useMouse({
4567
4738
  const handleMouseLeave = () => {
4568
4739
  mouse.set(center?.x ?? 0.5, center?.y ?? 0.5);
4569
4740
  };
4570
- const moveTarget = element ?? window;
4571
- moveTarget.addEventListener("mousemove", handleMouseMove);
4572
- moveTarget.addEventListener("mouseleave", handleMouseLeave);
4573
4741
  if (element) {
4742
+ element.addEventListener("mousemove", handleMouseMove);
4743
+ element.addEventListener("mouseleave", handleMouseLeave);
4574
4744
  element.addEventListener("mouseenter", updateRect);
4745
+ } else if (hasWindow) {
4746
+ window.addEventListener("mousemove", handleMouseMove);
4575
4747
  }
4576
4748
  if (hasWindow) {
4577
4749
  window.addEventListener("resize", handleResize);
4578
4750
  }
4579
4751
  return () => {
4580
- moveTarget.removeEventListener("mousemove", handleMouseMove);
4581
- moveTarget.removeEventListener("mouseleave", handleMouseLeave);
4582
4752
  if (element) {
4753
+ element.removeEventListener("mousemove", handleMouseMove);
4754
+ element.removeEventListener("mouseleave", handleMouseLeave);
4583
4755
  element.removeEventListener("mouseenter", updateRect);
4756
+ } else if (hasWindow) {
4757
+ window.removeEventListener("mousemove", handleMouseMove);
4584
4758
  }
4585
4759
  if (hasWindow) {
4586
4760
  window.removeEventListener("resize", handleResize);
@@ -4753,13 +4927,14 @@ function NoiseWarpPass({
4753
4927
  }
4754
4928
 
4755
4929
  // src/components/DitherPulseRing.tsx
4756
- import { jsx as jsx23 } from "react/jsx-runtime";
4930
+ import { Fragment as Fragment3, jsx as jsx23, jsxs as jsxs2 } from "react/jsx-runtime";
4757
4931
  function DitherPulseRing({
4758
4932
  spriteTextureSrc = DITHER_SPRITE_TEXTURE_SRC,
4759
4933
  glyphDitherEnabled = true,
4760
4934
  diffuseEnabled = false,
4761
4935
  blurEnabled = false,
4762
4936
  noiseWarpEnabled = false,
4937
+ combineMode,
4763
4938
  noiseWarpRadius,
4764
4939
  noiseWarpStrength,
4765
4940
  diffuseRadius,
@@ -4796,6 +4971,7 @@ function DitherPulseRing({
4796
4971
  diffuseEnabled,
4797
4972
  blurEnabled,
4798
4973
  noiseWarpEnabled,
4974
+ combineMode,
4799
4975
  noiseWarpRadius,
4800
4976
  noiseWarpStrength,
4801
4977
  diffuseRadius,
@@ -4821,6 +4997,7 @@ function DitherPulseRingContent({
4821
4997
  diffuseEnabled = false,
4822
4998
  blurEnabled = false,
4823
4999
  noiseWarpEnabled = false,
5000
+ combineMode,
4824
5001
  noiseWarpRadius,
4825
5002
  noiseWarpStrength,
4826
5003
  diffuseRadius,
@@ -4836,6 +5013,22 @@ function DitherPulseRingContent({
4836
5013
  ringPosition,
4837
5014
  ringAlpha
4838
5015
  }) {
5016
+ const normalizeHexColor = (hex) => {
5017
+ if (!hex) return null;
5018
+ const normalized = hex.replace("#", "");
5019
+ if (normalized.length !== 6) return null;
5020
+ const r = parseInt(normalized.slice(0, 2), 16) / 255;
5021
+ const g = parseInt(normalized.slice(2, 4), 16) / 255;
5022
+ const b = parseInt(normalized.slice(4, 6), 16) / 255;
5023
+ return [r, g, b];
5024
+ };
5025
+ const resolveColor = (color) => {
5026
+ if (!color) return null;
5027
+ if (typeof color === "string") {
5028
+ return normalizeHexColor(color);
5029
+ }
5030
+ return color;
5031
+ };
4839
5032
  const glyphUniforms = {};
4840
5033
  glyphUniforms.trackMouse = false;
4841
5034
  const noiseUniforms = {};
@@ -4859,49 +5052,529 @@ function DitherPulseRingContent({
4859
5052
  const borderUniforms = {};
4860
5053
  if (borderThickness !== void 0) borderUniforms.thickness = borderThickness;
4861
5054
  if (borderIntensity !== void 0) borderUniforms.intensity = borderIntensity;
4862
- if (borderColor) borderUniforms.color = borderColor;
5055
+ const resolvedBorderColor = resolveColor(borderColor);
5056
+ if (resolvedBorderColor) borderUniforms.color = resolvedBorderColor;
4863
5057
  if (borderDitherStrength !== void 0) {
4864
5058
  borderUniforms.ditherStrength = borderDitherStrength;
4865
5059
  }
4866
5060
  if (borderTonemap !== void 0) borderUniforms.tonemap = borderTonemap;
4867
5061
  if (borderAlpha !== void 0) borderUniforms.alpha = borderAlpha;
4868
5062
  const ringUniforms = {};
4869
- if (ringColor) ringUniforms.color = ringColor;
5063
+ const resolvedRingColor = resolveColor(ringColor);
5064
+ if (resolvedRingColor) ringUniforms.color = resolvedRingColor;
4870
5065
  if (ringSpeed !== void 0) ringUniforms.speed = ringSpeed;
4871
5066
  if (ringPosition) ringUniforms.position = ringPosition;
4872
5067
  if (ringAlpha !== void 0) ringUniforms.alpha = ringAlpha;
4873
- return /* @__PURE__ */ jsx23(
4874
- RenderPipeline,
5068
+ return /* @__PURE__ */ jsxs2(Fragment3, { children: [
5069
+ /* @__PURE__ */ jsx23(
5070
+ RenderPipeline,
5071
+ {
5072
+ passes: [
5073
+ {
5074
+ component: BorderBeamPass,
5075
+ props: { uniforms: borderUniforms }
5076
+ },
5077
+ {
5078
+ component: GlyphDitherPass,
5079
+ enabled: glyphDitherEnabled,
5080
+ props: { spriteTextureSrc, uniforms: glyphUniforms }
5081
+ }
5082
+ ],
5083
+ combine: combineMode ? { mode: combineMode } : void 0
5084
+ }
5085
+ ),
5086
+ /* @__PURE__ */ jsx23(
5087
+ RenderPipeline,
5088
+ {
5089
+ passes: [
5090
+ {
5091
+ component: BorderBeamPass,
5092
+ props: { uniforms: borderUniforms }
5093
+ },
5094
+ {
5095
+ component: ExpandingRingPass,
5096
+ props: { uniforms: ringUniforms }
5097
+ },
5098
+ {
5099
+ component: NoiseWarpPass,
5100
+ enabled: noiseWarpEnabled,
5101
+ props: { uniforms: noiseUniforms }
5102
+ },
5103
+ {
5104
+ component: GlyphDitherPass,
5105
+ enabled: glyphDitherEnabled,
5106
+ props: { spriteTextureSrc, uniforms: glyphUniforms }
5107
+ },
5108
+ {
5109
+ component: DiffusePass,
5110
+ enabled: diffuseEnabled,
5111
+ props: { uniforms: diffuseUniforms }
5112
+ },
5113
+ {
5114
+ component: BlurPass,
5115
+ enabled: blurEnabled,
5116
+ props: { uniforms: blurUniforms }
5117
+ }
5118
+ ],
5119
+ combine: combineMode ? { mode: combineMode } : void 0
5120
+ }
5121
+ )
5122
+ ] });
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,
4875
5208
  {
4876
- passes: [
4877
- {
4878
- component: BorderBeamPass,
4879
- props: { uniforms: borderUniforms }
4880
- },
4881
- {
4882
- component: ExpandingRingPass,
4883
- props: { clear: true, uniforms: ringUniforms }
4884
- },
4885
- {
4886
- component: NoiseWarpPass,
4887
- enabled: noiseWarpEnabled,
4888
- props: { uniforms: noiseUniforms }
4889
- },
4890
- {
4891
- component: GlyphDitherPass,
4892
- enabled: glyphDitherEnabled,
4893
- props: { spriteTextureSrc, uniforms: glyphUniforms }
4894
- },
4895
- {
4896
- component: DiffusePass,
4897
- enabled: diffuseEnabled,
4898
- props: { uniforms: diffuseUniforms }
4899
- },
4900
- {
4901
- component: BlurPass,
4902
- enabled: blurEnabled,
4903
- props: { uniforms: blurUniforms }
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;
4904
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
+ )
4905
5578
  ]
4906
5579
  }
4907
5580
  );
@@ -4909,6 +5582,7 @@ function DitherPulseRingContent({
4909
5582
  export {
4910
5583
  AnimatedDrawingSVG,
4911
5584
  DitherPulseRing,
5585
+ DitherStream,
4912
5586
  EFECTO_ASCII_COMPONENT_DEFAULTS,
4913
5587
  EFECTO_ASCII_POST_PROCESSING_DEFAULTS,
4914
5588
  Efecto,