253 lines
7.8 KiB
TypeScript
253 lines
7.8 KiB
TypeScript
"use client";
|
|
|
|
import React, { useMemo, useRef } from "react";
|
|
import { Canvas, useFrame } from "@react-three/fiber";
|
|
import * as THREE from "three";
|
|
|
|
// 1. Refined, clean Starfield (neat, elegant points of light)
|
|
function StarField({ count = 650 }: { count?: number }) {
|
|
const pointsRef = useRef<THREE.Points>(null!);
|
|
|
|
const [positions, colors] = useMemo(() => {
|
|
const pos = new Float32Array(count * 3);
|
|
const col = new Float32Array(count * 3);
|
|
|
|
const colorWhite = new THREE.Color("#ffffff");
|
|
const colorIcy = new THREE.Color("#93c5fd");
|
|
const colorSilver = new THREE.Color("#e2e8f0");
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
const radius = 30 + Math.random() * 80;
|
|
const theta = Math.random() * 2 * Math.PI;
|
|
const phi = Math.acos(2 * Math.random() - 1);
|
|
|
|
pos[i * 3] = radius * Math.sin(phi) * Math.cos(theta);
|
|
pos[i * 3 + 1] = radius * Math.sin(phi) * Math.sin(theta);
|
|
pos[i * 3 + 2] = radius * Math.cos(phi);
|
|
|
|
const r = Math.random();
|
|
const chosen = r > 0.7 ? colorIcy : r > 0.3 ? colorSilver : colorWhite;
|
|
|
|
col[i * 3] = chosen.r;
|
|
col[i * 3 + 1] = chosen.g;
|
|
col[i * 3 + 2] = chosen.b;
|
|
}
|
|
|
|
return [pos, col];
|
|
}, [count]);
|
|
|
|
useFrame((state, delta) => {
|
|
if (!pointsRef.current) return;
|
|
pointsRef.current.rotation.y += delta * 0.005;
|
|
pointsRef.current.rotation.x += delta * 0.002;
|
|
|
|
const targetX = (state.pointer.x * Math.PI) / 60;
|
|
const targetY = (state.pointer.y * Math.PI) / 60;
|
|
pointsRef.current.rotation.y += (targetX - pointsRef.current.rotation.y) * 0.02;
|
|
pointsRef.current.rotation.x += (-targetY - pointsRef.current.rotation.x) * 0.02;
|
|
});
|
|
|
|
return (
|
|
<points ref={pointsRef}>
|
|
<bufferGeometry>
|
|
<bufferAttribute attach="attributes-position" args={[positions, 3]} />
|
|
<bufferAttribute attach="attributes-color" args={[colors, 3]} />
|
|
</bufferGeometry>
|
|
<pointsMaterial
|
|
size={0.7}
|
|
vertexColors
|
|
transparent
|
|
opacity={0.8}
|
|
sizeAttenuation
|
|
depthWrite={false}
|
|
blending={THREE.AdditiveBlending}
|
|
/>
|
|
</points>
|
|
);
|
|
}
|
|
|
|
// 2. Bright, Glowing Background Planet (Astraea - Upper Right)
|
|
function BackgroundPlanetAstraea() {
|
|
const groupRef = useRef<THREE.Group>(null!);
|
|
const planetRef = useRef<THREE.Mesh>(null!);
|
|
const atmosphereRef = useRef<THREE.Mesh>(null!);
|
|
|
|
useFrame((state, delta) => {
|
|
if (planetRef.current) {
|
|
planetRef.current.rotation.y += delta * 0.05;
|
|
}
|
|
if (atmosphereRef.current) {
|
|
atmosphereRef.current.rotation.y -= delta * 0.03;
|
|
}
|
|
if (groupRef.current) {
|
|
// Subtle floating parallax
|
|
const targetX = 16 + (state.pointer.x * 2.5);
|
|
const targetY = 9 + (state.pointer.y * 2.0);
|
|
groupRef.current.position.x += (targetX - groupRef.current.position.x) * 0.02;
|
|
groupRef.current.position.y += (targetY - groupRef.current.position.y) * 0.02;
|
|
}
|
|
});
|
|
|
|
return (
|
|
<group ref={groupRef} position={[16, 9, -16]}>
|
|
{/* Bright Planet Core Sphere */}
|
|
<mesh ref={planetRef}>
|
|
<sphereGeometry args={[4.2, 48, 48]} />
|
|
<meshStandardMaterial
|
|
color="#38bdf8"
|
|
emissive="#0284c7"
|
|
emissiveIntensity={0.85}
|
|
roughness={0.25}
|
|
metalness={0.7}
|
|
/>
|
|
</mesh>
|
|
|
|
{/* Radiant Atmospheric Halo Rim */}
|
|
<mesh ref={atmosphereRef}>
|
|
<sphereGeometry args={[4.6, 32, 32]} />
|
|
<meshBasicMaterial
|
|
color="#7dd3fc"
|
|
transparent
|
|
opacity={0.35}
|
|
side={THREE.BackSide}
|
|
blending={THREE.AdditiveBlending}
|
|
/>
|
|
</mesh>
|
|
|
|
{/* Outer Luminous Glow Aura Ring */}
|
|
<mesh rotation={[Math.PI / 3, 0.4, 0]}>
|
|
<ringGeometry args={[4.8, 6.8, 48]} />
|
|
<meshBasicMaterial
|
|
color="#38bdf8"
|
|
transparent
|
|
opacity={0.22}
|
|
side={THREE.DoubleSide}
|
|
/>
|
|
</mesh>
|
|
</group>
|
|
);
|
|
}
|
|
|
|
// 3. Bright, Glowing Background Planet (Kore - Lower Left with Ring)
|
|
function BackgroundPlanetKore() {
|
|
const groupRef = useRef<THREE.Group>(null!);
|
|
const planetRef = useRef<THREE.Mesh>(null!);
|
|
const ringRef = useRef<THREE.Mesh>(null!);
|
|
|
|
useFrame((state, delta) => {
|
|
if (planetRef.current) {
|
|
planetRef.current.rotation.y += delta * 0.04;
|
|
}
|
|
if (ringRef.current) {
|
|
ringRef.current.rotation.z += delta * 0.02;
|
|
}
|
|
if (groupRef.current) {
|
|
// Gentle floating parallax
|
|
const targetX = -18 - (state.pointer.x * 3.0);
|
|
const targetY = -12 - (state.pointer.y * 2.5);
|
|
groupRef.current.position.x += (targetX - groupRef.current.position.x) * 0.02;
|
|
groupRef.current.position.y += (targetY - groupRef.current.position.y) * 0.02;
|
|
}
|
|
});
|
|
|
|
return (
|
|
<group ref={groupRef} position={[-18, -12, -22]}>
|
|
{/* Bright Violet Core Planet */}
|
|
<mesh ref={planetRef}>
|
|
<sphereGeometry args={[5.5, 48, 48]} />
|
|
<meshStandardMaterial
|
|
color="#a855f7"
|
|
emissive="#6366f1"
|
|
emissiveIntensity={0.75}
|
|
roughness={0.3}
|
|
metalness={0.8}
|
|
/>
|
|
</mesh>
|
|
|
|
{/* Atmospheric Halo */}
|
|
<mesh>
|
|
<sphereGeometry args={[5.9, 32, 32]} />
|
|
<meshBasicMaterial
|
|
color="#c084fc"
|
|
transparent
|
|
opacity={0.3}
|
|
side={THREE.BackSide}
|
|
blending={THREE.AdditiveBlending}
|
|
/>
|
|
</mesh>
|
|
|
|
{/* Luminous Majestic Planetary Ring */}
|
|
<mesh ref={ringRef} rotation={[Math.PI / 2.3, 0.35, 0]}>
|
|
<ringGeometry args={[6.8, 10.2, 64]} />
|
|
<meshStandardMaterial
|
|
color="#c084fc"
|
|
emissive="#a855f7"
|
|
emissiveIntensity={0.6}
|
|
transparent
|
|
opacity={0.45}
|
|
side={THREE.DoubleSide}
|
|
/>
|
|
</mesh>
|
|
</group>
|
|
);
|
|
}
|
|
|
|
// 4. Distant Luminous Stellar Moon Orb
|
|
function DistantLuminousOrb() {
|
|
const orbRef = useRef<THREE.Mesh>(null!);
|
|
|
|
useFrame((state, delta) => {
|
|
if (!orbRef.current) return;
|
|
const t = state.clock.getElapsedTime();
|
|
orbRef.current.position.y = 14 + Math.sin(t * 0.8) * 0.6;
|
|
orbRef.current.position.x = -8 + Math.cos(t * 0.5) * 0.8;
|
|
});
|
|
|
|
return (
|
|
<mesh ref={orbRef} position={[-8, 14, -18]}>
|
|
<sphereGeometry args={[1.2, 24, 24]} />
|
|
<meshStandardMaterial
|
|
color="#34d399"
|
|
emissive="#10b981"
|
|
emissiveIntensity={1.2}
|
|
roughness={0.1}
|
|
/>
|
|
</mesh>
|
|
);
|
|
}
|
|
|
|
export default function CosmicGalaxy() {
|
|
return (
|
|
<div className="fixed inset-0 pointer-events-none z-0 overflow-hidden">
|
|
<Canvas
|
|
camera={{ position: [0, 0, 32], fov: 50 }}
|
|
dpr={[1, 1.5]}
|
|
gl={{ antialias: true, alpha: true, powerPreference: "high-performance" }}
|
|
className="w-full h-full"
|
|
>
|
|
<color attach="background" args={["#0a0a12"]} />
|
|
|
|
{/* Cinematic Starlight Illumination */}
|
|
<ambientLight intensity={0.5} />
|
|
<directionalLight position={[20, 20, 10]} intensity={1.8} color="#e0f2fe" />
|
|
<directionalLight position={[-20, -15, -5]} intensity={1.0} color="#c084fc" />
|
|
|
|
{/* Clean, Neat Starfield */}
|
|
<StarField count={650} />
|
|
|
|
{/* Bright Circular Planet-Like Objects in the Background */}
|
|
<BackgroundPlanetAstraea />
|
|
<BackgroundPlanetKore />
|
|
<DistantLuminousOrb />
|
|
</Canvas>
|
|
|
|
{/* Atmospheric depth vignettes (subtle gradient, no harsh boxes) */}
|
|
<div className="absolute inset-0 bg-radial from-transparent via-[#0a0a12]/30 to-[#0a0a12] pointer-events-none" />
|
|
|
|
{/* Soft circular glowing light fountains behind the planets */}
|
|
<div className="absolute -top-10 right-10 w-96 h-96 bg-cyan-500/10 rounded-full blur-[100px] pointer-events-none" />
|
|
<div className="absolute -bottom-10 left-10 w-[450px] h-[450px] bg-purple-600/10 rounded-full blur-[120px] pointer-events-none" />
|
|
</div>
|
|
);
|
|
}
|