318 lines
9.6 KiB
TypeScript
318 lines
9.6 KiB
TypeScript
"use client";
|
|
|
|
import { Canvas, useFrame, useThree } from "@react-three/fiber";
|
|
import { useEffect, useMemo, useRef } from "react";
|
|
import * as THREE from "three";
|
|
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface ShaderPlaneProps {
|
|
vertexShader: string;
|
|
fragmentShader: string;
|
|
uniforms: { [key: string]: { value: unknown } };
|
|
}
|
|
|
|
const ShaderPlane = ({
|
|
vertexShader,
|
|
fragmentShader,
|
|
uniforms,
|
|
}: ShaderPlaneProps) => {
|
|
const meshRef = useRef<THREE.Mesh>(null);
|
|
const { size } = useThree();
|
|
const mousePos = useRef({ x: 0, y: 0 });
|
|
const targetPos = useRef({ x: 0, y: 0 });
|
|
|
|
useEffect(() => {
|
|
const handleMouseMove = (event: MouseEvent) => {
|
|
targetPos.current.x = (event.clientX / window.innerWidth) * 2 - 1;
|
|
targetPos.current.y = -(event.clientY / window.innerHeight) * 2 + 1;
|
|
};
|
|
|
|
window.addEventListener("mousemove", handleMouseMove);
|
|
return () => window.removeEventListener("mousemove", handleMouseMove);
|
|
}, []);
|
|
|
|
useFrame((state) => {
|
|
if (meshRef.current) {
|
|
const material = meshRef.current.material as THREE.ShaderMaterial;
|
|
material.uniforms.u_time.value = state.clock.elapsedTime * 0.5;
|
|
material.uniforms.u_resolution.value.set(size.width, size.height, 1.0);
|
|
|
|
const lerpFactor = 0.025;
|
|
mousePos.current.x +=
|
|
(targetPos.current.x - mousePos.current.x) * lerpFactor;
|
|
mousePos.current.y +=
|
|
(targetPos.current.y - mousePos.current.y) * lerpFactor;
|
|
material.uniforms.u_mouse.value.set(
|
|
mousePos.current.x,
|
|
mousePos.current.y,
|
|
0.0,
|
|
0.0,
|
|
);
|
|
}
|
|
});
|
|
|
|
return (
|
|
<mesh ref={meshRef}>
|
|
<planeGeometry args={[2, 2]} />
|
|
<shaderMaterial
|
|
vertexShader={vertexShader}
|
|
fragmentShader={fragmentShader}
|
|
uniforms={uniforms}
|
|
side={THREE.FrontSide}
|
|
depthTest={false}
|
|
depthWrite={false}
|
|
/>
|
|
</mesh>
|
|
);
|
|
};
|
|
|
|
interface ShaderBackgroundProps {
|
|
vertexShader?: string;
|
|
fragmentShader?: string;
|
|
uniforms?: { [key: string]: { value: unknown } };
|
|
className?: string;
|
|
color?: string;
|
|
}
|
|
|
|
const Shader3 = ({
|
|
vertexShader = `
|
|
varying vec2 vUv;
|
|
void main() {
|
|
vUv = uv;
|
|
gl_Position = vec4(position, 1.0);
|
|
}
|
|
`,
|
|
fragmentShader = `
|
|
precision highp float;
|
|
const float CIRCLE_SCALE = 1.0;
|
|
|
|
varying vec2 vUv;
|
|
uniform float u_time;
|
|
uniform vec3 u_resolution;
|
|
uniform vec4 u_mouse;
|
|
uniform vec3 u_color;
|
|
uniform sampler2D u_channel0;
|
|
|
|
#define R u_resolution
|
|
#define PI 3.141528
|
|
|
|
const float refractIndex = 0.3;
|
|
|
|
vec3 hsv(float h,float s,float v){return ((clamp(abs(fract(h+vec3(0.,.666,.333))*6.-3.)-1.,0.,1.)-1.)*s+1.)*v;}
|
|
vec3 rgb2hsv(vec3 c){
|
|
vec4 K = vec4(0.0, -1.0/3.0, 2.0/3.0, -1.0);
|
|
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
|
|
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
|
|
float d = q.x - min(q.w, q.y);
|
|
float e = 1.0e-10;
|
|
return vec3(abs(q.z + (q.w - q.y) / (6.0*d + e)), d / (q.x + e), q.x);
|
|
}
|
|
|
|
float map(float val, float inA, float inB, float outA, float outB) {
|
|
return (val - inA) / (inB - inA) * (outB - outA) + outA;
|
|
}
|
|
|
|
float fresnel(vec3 direction, vec3 normal, float power, bool invert) {
|
|
vec3 halfDirection = normalize( normal + direction );
|
|
float cosine = dot( halfDirection, direction );
|
|
float product = max( cosine, 0.0 );
|
|
float factor = invert ? 1.0 - pow( product, power ) : pow( product, power );
|
|
return factor;
|
|
}
|
|
vec4 remapShadows(vec4 color) {
|
|
float factor = 10.;
|
|
return vec4(
|
|
pow(color.x, factor),
|
|
pow(color.y, factor),
|
|
pow(color.z, factor),
|
|
color.w
|
|
);
|
|
}
|
|
float specular(vec3 eyeVector, vec3 worldNormal, vec3 light, float shininess, float diffuseness) {
|
|
vec3 normal = worldNormal;
|
|
vec3 lightVector = normalize(-light);
|
|
vec3 halfVector = normalize(eyeVector + lightVector);
|
|
float NdotL = dot(normal, lightVector);
|
|
float NdotH = dot(normal, halfVector);
|
|
float kDiffuse = max(0.0, NdotL);
|
|
float NdotH2 = NdotH * NdotH;
|
|
float kSpecular = pow(NdotH2, shininess);
|
|
return kSpecular + kDiffuse * diffuseness;
|
|
}
|
|
|
|
vec4 sampleBackground(vec3 normal, sampler2D bg) {
|
|
float phi = acos(normal.y);
|
|
float sinPhi = sin(phi);
|
|
float theta =
|
|
abs(sinPhi) > 0.0001
|
|
? acos(normal.x / sinPhi)
|
|
: 0.;
|
|
vec2 coord = vec2(
|
|
map(theta, 0., PI, 0., 1.),
|
|
map(phi, 0., PI, 1., 0.)
|
|
);
|
|
|
|
return texture(bg, coord);
|
|
}
|
|
|
|
float lambert(vec3 normal, vec3 lightPos){
|
|
return max(dot(normal, lightPos), 0.05);
|
|
}
|
|
|
|
float hash12(vec2 p)
|
|
{
|
|
vec3 p3 = fract(vec3(p.xyx) * .1031);
|
|
p3 += dot(p3, p3.yzx + 33.33);
|
|
return fract((p3.x + p3.y) * p3.z);
|
|
}
|
|
|
|
vec2 globalPos(vec2 pos){
|
|
vec2 mover = u_mouse.xy * 0.25;
|
|
return pos-mover + hash12(gl_FragCoord.xy + u_time * 0.1)*.0125;
|
|
}
|
|
|
|
float circle(vec2 pos,float lo, float hi){
|
|
return smoothstep(lo,hi,length(globalPos(pos)));
|
|
}
|
|
|
|
vec3 gradient(float d){
|
|
float a = smoothstep(0.0, 1.0, d);
|
|
vec3 colA = mix(vec3(0.05,0.05,0.05),vec3(.4,.6,.6),smoothstep(0.05,0.05,0.05) * d);
|
|
vec3 colB = mix(u_color * 0.06, u_color * 0.9, a);
|
|
return mix(colA, colB, d);
|
|
}
|
|
|
|
vec3 coloredRim(vec2 pos, float lamVal) {
|
|
float rimRadius = 0.035 * CIRCLE_SCALE + lamVal * 0.05 * CIRCLE_SCALE;
|
|
float innerRadius = 0.03 * CIRCLE_SCALE + lamVal * 0.025 * CIRCLE_SCALE;
|
|
float dist = length(globalPos(pos));
|
|
|
|
float rimMask = smoothstep(innerRadius, rimRadius, dist) * (1.0 - smoothstep(rimRadius, rimRadius + 0.05, dist));
|
|
|
|
vec3 hsvColor = rgb2hsv(u_color);
|
|
float baseHue = hsvColor.x;
|
|
float sat = hsvColor.y;
|
|
float hue = baseHue + dist * 2.0;
|
|
vec3 rimColor = hsv(hue, sat, 1.0);
|
|
|
|
return rimColor * rimMask;
|
|
}
|
|
|
|
float sdfCircle(vec2 pos, float r) {
|
|
return length(globalPos(pos)) - r;
|
|
}
|
|
vec2 sdfNormal(vec2 pos, float r) {
|
|
vec2 e = vec2(1.0/R.x, 1.0/R.y) * 1.5;
|
|
float dx = sdfCircle(pos + vec2(e.x, 0.0), r) - sdfCircle(pos - vec2(e.x, 0.0), r);
|
|
float dy = sdfCircle(pos + vec2(0.0, e.y), r) - sdfCircle(pos - vec2(0.0, e.y), r);
|
|
vec2 n = normalize(vec2(dx, dy));
|
|
return n;
|
|
}
|
|
vec3 shadeAt(vec2 pos, float lamVal) {
|
|
float dLocal = 1. - circle(pos, 0.05 * CIRCLE_SCALE, .3 * CIRCLE_SCALE + lamVal * 0.025 * CIRCLE_SCALE);
|
|
return gradient(dLocal);
|
|
}
|
|
vec3 refractSDFColor(vec2 pos, float lamVal) {
|
|
float shiftPxR = 2.0;
|
|
float shiftPxG = 0.0;
|
|
float shiftPxB = -2.0;
|
|
|
|
float r = 0.175 * CIRCLE_SCALE + lamVal * 0.6125 * CIRCLE_SCALE;
|
|
vec2 n2 = normalize(sdfNormal(pos, r));
|
|
|
|
vec2 px = 1.0 / R.xy;
|
|
vec3 cR = shadeAt(pos + n2 * px * shiftPxR, lamVal);
|
|
vec3 cG = shadeAt(pos + n2 * px * shiftPxG, lamVal);
|
|
vec3 cB = shadeAt(pos + n2 * px * shiftPxB, lamVal);
|
|
return vec3(cR.r, cG.g, cB.b);
|
|
}
|
|
|
|
void mainImage( out vec4 fragColor, in vec2 fragCoord )
|
|
{
|
|
vec2 uv = fragCoord / R.xy;
|
|
vec2 p = (fragCoord/R.xy-.5)*vec2(R.x/R.y,1.)*2.;
|
|
|
|
float angle = PI / 4.0;
|
|
float cosA = cos(angle);
|
|
float sinA = sin(angle);
|
|
vec2 rotatedP = vec2(
|
|
p.x * cosA - p.y * sinA,
|
|
p.x * sinA + p.y * cosA
|
|
);
|
|
|
|
vec3 rd = vec3(0.0, 0.0, -1.0);
|
|
float rod_x = fract(rotatedP.x * 2.) * 2.0 - 1.0;
|
|
float rod_z = sqrt(1.0 - rod_x*rod_x);
|
|
vec3 n = vec3(rod_x, 0.0, -rod_z);
|
|
|
|
vec3 refracted_ray = mix(n, rd, refractIndex);
|
|
float dist = 0.8;
|
|
float z_dist = dist / (refracted_ray.z - 0.8);
|
|
vec3 pos = vec3(p, 0.0) + z_dist*refracted_ray;
|
|
vec2 subPos = vec2(pos.xy * pos.z);
|
|
|
|
vec3 lpos = normalize(vec3(0., 0., -1.0));
|
|
vec3 lcol = vec3(1.);
|
|
float lambert = pow(lambert(n,lpos),2.);
|
|
vec3 reflectedDir = normalize(reflect(rd, n));
|
|
float fresnel = fresnel(rd, n, 8.,true);
|
|
float fresnelStrength = 0.595;
|
|
|
|
float d = 1.-circle(subPos,0.05 * CIRCLE_SCALE,.3 * CIRCLE_SCALE + lambert * 0.0125 *CIRCLE_SCALE);
|
|
vec3 color = gradient(d);
|
|
|
|
vec3 rimColor = coloredRim(subPos, lambert);
|
|
color += rimColor * 0.65;
|
|
|
|
float g = 1.0 - abs(n.z);
|
|
g = g * 0.8 / (g * 0.8 - g + 1.0);
|
|
float glass = 1.-((1.0 - 0.3 * g) - g * hash12(gl_FragCoord.xy + d));
|
|
vec3 refrCol = refractSDFColor(subPos, lambert);
|
|
color = mix(color, refrCol, 0.65);
|
|
color += lambert * color * 0.05 * d;
|
|
color += color * glass * d * 0.05;
|
|
color = clamp(color,0.,1.);
|
|
|
|
color = pow(color,vec3(1./1.222));
|
|
|
|
fragColor = vec4(vec3(color),1.0);
|
|
}
|
|
|
|
void main() {
|
|
vec4 fragColor;
|
|
vec2 fragCoord = vUv * u_resolution.xy;
|
|
mainImage(fragColor, fragCoord);
|
|
gl_FragColor = fragColor;
|
|
}
|
|
`,
|
|
uniforms = {},
|
|
className,
|
|
color = "#bbffcc",
|
|
}: ShaderBackgroundProps) => {
|
|
const shaderUniforms = useMemo(
|
|
() => ({
|
|
u_time: { value: 0 },
|
|
u_resolution: { value: new THREE.Vector3(1, 1, 1) },
|
|
u_mouse: { value: new THREE.Vector4(0, 0, 0, 0) },
|
|
u_color: { value: new THREE.Color(color) },
|
|
...uniforms,
|
|
}),
|
|
[uniforms, color],
|
|
);
|
|
|
|
return (
|
|
<section className={cn(className, "absolute inset-0 h-screen w-full")}>
|
|
<Canvas>
|
|
<ShaderPlane
|
|
vertexShader={vertexShader}
|
|
fragmentShader={fragmentShader}
|
|
uniforms={shaderUniforms}
|
|
/>
|
|
</Canvas>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export { Shader3 };
|