{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "water-caustic",
  "title": "Water Caustic",
  "description": "A stunning, tileable water caustic lighting shader using wave interference.",
  "dependencies": [
    "three",
    "@react-three/fiber",
    "clsx",
    "tailwind-merge"
  ],
  "files": [
    {
      "path": "registry/chamaac/water-caustic/water-caustic.tsx",
      "content": "\"use client\";\n\nimport { useRef, useMemo, useEffect } from \"react\";\nimport { Canvas, useFrame } from \"@react-three/fiber\";\nimport * as THREE from \"three\";\nimport { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nfunction cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n\nconst vertexShader = `\nvarying vec2 vUv;\nvoid main() {\n  vUv = uv;\n  gl_Position = vec4(position, 1.0);\n}\n`;\n\nconst fragmentShader = `\nuniform float iTime;\nuniform vec2 iResolution;\nuniform vec3 uColor;\nvarying vec2 vUv;\n\n// Tileable 2D Hash function\nvec2 hash2(vec2 p, float period) {\n    p = mod(p, period);\n    p = vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)));\n    return fract(sin(p) * 43758.5453123);\n}\n\n// Tileable Voronoi calculating F2 - F1 for sharp caustic ridges\nfloat causticPattern(vec2 p, float period, float t) {\n    vec2 n = floor(p);\n    vec2 f = fract(p);\n    \n    float F1 = 8.0;\n    float F2 = 8.0;\n    \n    for (int j = -1; j <= 1; j++) {\n        for (int i = -1; i <= 1; i++) {\n            vec2 g = vec2(float(i), float(j));\n            vec2 o = hash2(n + g, period);\n            \n            // Animate with constrained jitter to prevent cell-crossing artifacts\n            o = 0.5 + 0.4 * sin(t + 6.2831853 * o);\n            \n            vec2 r = g + o - f;\n            float d = dot(r, r);\n            \n            if (d < F1) {\n                F2 = F1;\n                F1 = d;\n            } else if (d < F2) {\n                F2 = d;\n            }\n        }\n    }\n    return F2 - F1;\n}\n\nvoid mainImage(out vec4 fragColor, in vec2 fragCoord) {\n    // Normalize coordinates\n    vec2 uv = fragCoord.xy / iResolution.xy;\n    \n    // Grid scale and matching period for perfect tiling\n    float period = 4.0;\n    vec2 p = uv * period;\n    \n    // Time scaling for fluid motion\n    float t = iTime * 1.0;\n    \n    // Layer 1: Base large caustics\n    float c1 = causticPattern(p, period, t);\n    \n    // Layer 2: Smaller, faster overlapping caustics\n    float c2 = causticPattern(p * 5.0, period * 5.0, t * 1.4);\n    \n    // Layer 3: Micro details\n    float c3 = causticPattern(p * 4.0, period * 4.0, t * 0.7);\n    \n    // Combine layers beautifully\n    // We invert the result (1.0 - c) to make the ridges bright and centers dark\n    float caustics = 0.0;\n    caustics += (1.0 - c1) * 0.6;\n    caustics += (1.0 - c2) * 0.3;\n    caustics += (1.0 - c3) * 0.1;\n    \n    // Sharpen the highlights more gently for a softer, organic look\n    caustics = pow(caustics, 3.0);\n    \n    // Enhance contrast without harsh clipping\n    caustics = smoothstep(0.3, 1.0, caustics) * 1.2;\n    \n    // Use the dynamic unified color from React props\n    // A pure, luminous color works perfectly to give a high-end, clean aesthetic over dark backgrounds\n    vec3 baseColor = uColor;\n    \n    vec3 color = baseColor * caustics;\n    \n    // Softly glow the core of the caustics \n    // using a highly reduced power multiplier so it doesn't get overly intense\n    color += baseColor * vec3(0.5) * pow(caustics, 2.0);\n    \n    // Gentle alpha mapping for subtle translucent blending\n    float alpha = clamp(caustics * 0.9, 0.0, 1.0);\n    \n    fragColor = vec4(color, alpha);\n}\n\nvoid main() {\n    mainImage(gl_FragColor, vUv * iResolution);\n}\n`;\n\nconst ShaderPlane = ({ color }: { color: string }) => {\n  const materialRef = useRef<THREE.ShaderMaterial>(null);\n\n  const uniforms = useMemo(\n    () => ({\n      iTime: { value: 0 },\n      iResolution: { value: new THREE.Vector2() },\n      uColor: { value: new THREE.Color(color) },\n    }),\n    [color]\n  );\n\n  useEffect(() => {\n    uniforms.uColor.value.set(color);\n  }, [color, uniforms]);\n\n  useFrame((state) => {\n    if (materialRef.current) {\n      materialRef.current.uniforms.iTime.value = state.clock.elapsedTime;\n      materialRef.current.uniforms.iResolution.value.set(\n        state.size.width * state.viewport.dpr,\n        state.size.height * state.viewport.dpr\n      );\n    }\n  });\n\n  return (\n    <mesh>\n      <planeGeometry args={[2, 2]} />\n      <shaderMaterial\n        ref={materialRef}\n        vertexShader={vertexShader}\n        fragmentShader={fragmentShader}\n        uniforms={uniforms}\n        depthWrite={false}\n        depthTest={false}\n        transparent={true}\n        blending={THREE.AdditiveBlending}\n      />\n    </mesh>\n  );\n};\n\nexport interface WaterCausticProps {\n  className?: string;\n  color?: string;\n}\n\nexport default function WaterCaustic({\n  className,\n  color = \"#ffffff\",\n}: WaterCausticProps) {\n  return (\n    <div\n      className={cn(\n        \"w-full h-full absolute inset-0 pointer-events-none\",\n        className\n      )}\n    >\n      <Canvas\n        camera={{ position: [0, 0, 1] }}\n        gl={{ antialias: false, alpha: true }}\n        dpr={[1, 2]}\n      >\n        <ShaderPlane color={color} />\n      </Canvas>\n    </div>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}