"use client"; import { useMemo, useRef } from "react"; import { Canvas, useFrame, useThree } from "@react-three/fiber"; import { MeshDistortMaterial, Sphere, Preload, Float } from "@react-three/drei"; import { EffectComposer, Bloom, Vignette } from "@react-three/postprocessing"; import * as THREE from "three"; // --- CONFIGURATION --- const NEURON_COUNT = 15; // Keep low for high-quality geometry const CONNECTION_DISTANCE = 6.5; const PULSE_SPEED = 1.8; // 1. REALISTIC CELL (Soma + Nucleus) function NeuronCell({ position }: { position: [number, number, number] }) { const membraneRef = useRef(null!); const nucleusRef = useRef(null!); // Randomize biology const size = useMemo(() => 0.5 + Math.random() * 0.3, []); useFrame((state) => { const t = state.clock.getElapsedTime(); // Biological breathing/pulsing if (membraneRef.current) { const scale = size + Math.sin(t * 2 + position[0]) * 0.05; membraneRef.current.scale.setScalar(scale); } // Nucleus gentle pulse (faster) if (nucleusRef.current) { const nScale = (size * 0.4) + Math.sin(t * 4) * 0.02; nucleusRef.current.scale.setScalar(nScale); } }); return ( {/* A. The Membrane (Outer Shell) */} {/* B. The Nucleus (Inner Core) */} ); } // 2. SYNAPSE (Axon + Electric Pulse) function Synapse({ start, end }: { start: THREE.Vector3; end: THREE.Vector3 }) { const curve = useMemo(() => { // Create organic curve const mid = new THREE.Vector3().lerpVectors(start, end, 0.5); // Randomize control point for "tendon" shape mid.add(new THREE.Vector3( (Math.random() - 0.5) * 3, (Math.random() - 0.5) * 3, (Math.random() - 0.5) * 3 )); return new THREE.QuadraticBezierCurve3(start, mid, end); }, [start, end]); const points = useMemo(() => curve.getPoints(30), [curve]); const impulseRef = useRef(null!); useFrame((state) => { // Move electric pulse const t = (state.clock.getElapsedTime() * PULSE_SPEED) % 1; if (impulseRef.current) { const pos = curve.getPointAt(t); impulseRef.current.position.copy(pos); // Stretch effect for speed illusion const tangent = curve.getTangent(t).normalize(); impulseRef.current.lookAt(pos.clone().add(tangent)); impulseRef.current.scale.set(0.6, 0.6, 2.5); // Stretch Z } }); return ( {/* The physical connection (Axon) */} [p.x, p.y, p.z]))} itemSize={3} /> {/* The Traveling Spark (Electricity) */} ); } // 3. BACKGROUND PARTICLES (Neurotransmitters) function NeuroDust() { const count = 200; const mesh = useRef(null!); const particles = useMemo(() => { const temp = []; for(let i=0; i new THREE.Object3D(), []); useFrame((state) => { particles.forEach((particle, i) => { let { t, factor, speed, x, y, z } = particle; t = particle.t += speed / 2; const a = Math.cos(t) + Math.sin(t * 1) / 10; const b = Math.sin(t) + Math.cos(t * 2) / 10; const s = Math.cos(t); dummy.position.set( x + Math.cos(t) + Math.sin(t) * 2, y + Math.sin(t) + Math.cos(t) * 2, z + Math.cos(t) ); dummy.scale.setScalar(s * 0.03); // Tiny dust dummy.rotation.set(s * 5, s * 5, s * 5); dummy.updateMatrix(); if (mesh.current) mesh.current.setMatrixAt(i, dummy.matrix); }); if (mesh.current) mesh.current.instanceMatrix.needsUpdate = true; }); return ( ); } // 4. MAIN SCENE function Scene() { const { viewport } = useThree(); // Generate random positions for neurons const neurons = useMemo(() => { const temp = []; for (let i = 0; i < NEURON_COUNT; i++) { temp.push({ position: new THREE.Vector3( (Math.random() - 0.5) * 22, (Math.random() - 0.5) * 22, (Math.random() - 0.5) * 10 ), id: i }); } return temp; }, []); // Connect them const connections = useMemo(() => { const conns = []; for(let i=0; i { // Subtle Camera Parallax based on Mouse const x = (state.pointer.x * viewport.width) / 12; const y = (state.pointer.y * viewport.height) / 12; state.camera.position.x = THREE.MathUtils.lerp(state.camera.position.x, x, 0.02); state.camera.position.y = THREE.MathUtils.lerp(state.camera.position.y, y, 0.02); state.camera.lookAt(0,0,0); }); return ( <> {/* Match gray-950 */} {neurons.map(n => )} {connections.map(c => )} {/* POST PROCESSING - The Secret Sauce for Realism */} ); } export default function RealisticNeurons() { return (
); }