import React, { useRef, useMemo } from 'react'; import { Canvas, useFrame, useThree } from '@react-three/fiber'; import * as THREE from 'three'; const vertexShader = ` varying vec2 vUv; uniform float time; uniform vec4 resolution; void main() { vUv = uv; gl_Position = vec4(position, 1.0); } `; const fragmentShader = ` precision highp float; varying vec2 vUv; uniform float time; uniform vec4 resolution; float PI = 3.141592653589793238; mat4 rotationMatrix(vec3 axis, float angle) { axis = normalize(axis); float s = sin(angle); float c = cos(angle); float oc = 1.0 - c; return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0, oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0, oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0, 0.0, 0.0, 0.0, 1.0); } vec3 rotate(vec3 v, vec3 axis, float angle) { mat4 m = rotationMatrix(axis, angle); return (m * vec4(v, 1.0)).xyz; } float smin( float a, float b, float k ) { k *= 6.0; float h = max( k-abs(a-b), 0.0 )/k; return min(a,b) - h*h*h*k*(1.0/6.0); } float sphereSDF(vec3 p, float r) { return length(p) - r; } float sdf(vec3 p) { vec3 p1 = rotate(p, vec3(0.0, 0.0, 1.0), time/5.0); vec3 p2 = rotate(p, vec3(1.), -time/5.0); vec3 p3 = rotate(p, vec3(1., 1., 0.), -time/4.5); vec3 p4 = rotate(p, vec3(0., 1., 0.), -time/4.0); float final = sphereSDF(p1 - vec3(-0.5, 0.0, 0.0), 0.35); float nextSphere = sphereSDF(p2 - vec3(0.55, 0.0, 0.0), 0.3); final = smin(final, nextSphere, 0.1); nextSphere = sphereSDF(p2 - vec3(-0.8, 0.0, 0.0), 0.2); final = smin(final, nextSphere, 0.1); nextSphere = sphereSDF(p3 - vec3(1.0, 0.0, 0.0), 0.15); final = smin(final, nextSphere, 0.1); nextSphere = sphereSDF(p4 - vec3(0.45, -0.45, 0.0), 0.15); final = smin(final, nextSphere, 0.1); return final; } vec3 getNormal(vec3 p) { float d = 0.001; return normalize(vec3( sdf(p + vec3(d, 0.0, 0.0)) - sdf(p - vec3(d, 0.0, 0.0)), sdf(p + vec3(0.0, d, 0.0)) - sdf(p - vec3(0.0, d, 0.0)), sdf(p + vec3(0.0, 0.0, d)) - sdf(p - vec3(0.0, 0.0, d)) )); } float rayMarch(vec3 rayOrigin, vec3 ray) { float t = 0.0; for (int i = 0; i < 100; i++) { vec3 p = rayOrigin + ray * t; float d = sdf(p); if (d < 0.001) return t; t += d; if (t > 100.0) break; } return -1.0; } void main() { vec2 newUV = (vUv - vec2(0.5)) * resolution.zw + vec2(0.5); vec3 cameraPos = vec3(0.0, 0.0, 5.0); vec3 ray = normalize(vec3((vUv - vec2(0.5)) * resolution.zw, -1)); vec3 color = vec3(1.0); float t = rayMarch(cameraPos, ray); if (t > 0.0) { vec3 p = cameraPos + ray * t; vec3 normal = getNormal(p); float fresnel = pow(1.0 + dot(ray, normal), 3.0); color = vec3(fresnel); gl_FragColor = vec4(color, 1.0); } else { gl_FragColor = vec4(1.0); } } `; function LavaLampShader() { const meshRef = useRef(); const { size } = useThree(); const uniforms = useMemo(() => ({ time: { value: 0 }, resolution: { value: new THREE.Vector4() } }), []); // Update resolution when size changes React.useEffect(() => { const { width, height } = size; const imageAspect = 1; let a1, a2; if (height / width > imageAspect) { a1 = (width / height) * imageAspect; a2 = 1; } else { a1 = 1; a2 = (height / width) / imageAspect; } uniforms.resolution.value.set(width, height, a1, a2); }, [size, uniforms]); useFrame((state) => { if (meshRef.current) { uniforms.time.value = state.clock.elapsedTime; } }); return ( ); } export const LavaLamp = () => { return (
); }