import React, { useRef, useMemo } from 'react';
import { Canvas, useFrame } from '@react-three/fiber';
import { OrbitControls, Sky } from '@react-three/drei';
import { useControls } from 'leva';
import * as THREE from 'three';

function ShaderMesh() {
  const meshRef = useRef();

  const { thresholdX, thresholdY, displacement, baseColor, highlightColor, wireframe, transparent, autoRotate
  } = useControls({
    thresholdX: { value: 0.5, min: 0, max: 1, step: 0.01 },
    thresholdY: { value: 0.5, min: 0, max: 1, step: 0.01 },
    displacement: { value: 0.1, min: 0, max: 1, step: 0.01 },
    baseColor: '#d8d5d5',
    highlightColor: '#00ff00',
    wireframe: true,
    transparent: true,
    autoRotate: false
  });

  const material = useMemo(() => {
    return new THREE.ShaderMaterial({
      vertexShader: `
        uniform float uThresholdX;
        uniform float uThresholdY;
        uniform float uDisplacement;
        varying vec2 vUv; 
        
        void main() {
          vec3 newPosition = position;
          
          if (uv.x > uThresholdX && uv.y > uThresholdY) {
            newPosition.z += uDisplacement;
          }
        
          gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
          vUv = uv;
        }
      `,
      fragmentShader: `
        uniform float uThresholdX;
        uniform float uThresholdY;
        uniform vec3 uBaseColor;
        uniform vec3 uHighlightColor;
        varying vec2 vUv; 
      
        void main() {
          vec3 color = uBaseColor;
          
          if (vUv.x > uThresholdX && vUv.y > uThresholdY) {
            color = uHighlightColor;
          }
        
          gl_FragColor = vec4(color, 1.0);
        }
      `,
      uniforms: {
        uThresholdX: { value: thresholdX },
        uThresholdY: { value: thresholdY },
        uDisplacement: { value: displacement },
        uBaseColor: { value: new THREE.Color(baseColor) },
        uHighlightColor: { value: new THREE.Color(highlightColor) }
      },
      side: THREE.DoubleSide,
      wireframe,
      transparent
    });
  }, [thresholdX, thresholdY, displacement, baseColor, highlightColor, wireframe, transparent]);

  useFrame((state) => {
    if (meshRef.current && autoRotate) {
      meshRef.current.rotation.x = state.clock.elapsedTime * 0.3;
      meshRef.current.rotation.y = state.clock.elapsedTime * 0.2;
    }

    // Update uniforms
    if (material.uniforms) {
      material.uniforms.uThresholdX.value = thresholdX;
      material.uniforms.uThresholdY.value = thresholdY;
      material.uniforms.uDisplacement.value = displacement;
      material.uniforms.uBaseColor.value.set(baseColor);
      material.uniforms.uHighlightColor.value.set(highlightColor);
    }
  });

  return (
    <mesh ref={meshRef} material={material}>
      <planeGeometry args={[3, 3, 32, 32]} />
    </mesh>
  );
}

function Scene() {
  return (
    <Canvas camera={{ position: [0, 0, 4], fov: 50 }} style={{ height: '90vh' }}>
      <ambientLight intensity={0.5} />
      <pointLight position={[10, 10, 10]} />
      <Sky sunPosition={[0, 1, 0]} />
      <OrbitControls enableDamping dampingFactor={0.05} />
      <ShaderMesh />
    </Canvas>
  );
}

export default function App() {
  return (
    <div className="w-full h-screen bg-gray-900 relative">
      <Scene />
      <div className="absolute top-4 left-4 text-white">
        <h1 className="text-xl font-bold mb-2">Interactive Shader Demo</h1>
      </div>
    </div>
  );
}

Comments (0)

Loading comments...