import * as THREE from 'three';
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x040810);
const camera = new THREE.PerspectiveCamera(45, 1, 0.1, 100);
camera.position.set(0, 0.4, 4.6);
camera.lookAt(0, 0, 0);
const vertexShader = [
'varying vec3 vNormal;',
'varying vec3 vViewDir;',
'varying vec2 vUvV;',
'void main() {',
' vNormal = normalize(normalMatrix * normal);',
' vec4 mv = modelViewMatrix * vec4(position, 1.0);',
' vViewDir = normalize(-mv.xyz);',
' vUvV = uv;',
' gl_Position = projectionMatrix * mv;',
'}',
].join('\n');
const fragmentShader = [
'uniform float uTime;',
'varying vec3 vNormal;',
'varying vec3 vViewDir;',
'varying vec2 vUvV;',
'float hash(float x) { return fract(sin(x) * 43758.5453); }',
'void main() {',
' vec3 n = normalize(vNormal);',
' vec3 v = normalize(vViewDir);',
' float fresnel = pow(1.0 - max(dot(n, v), 0.0), 2.2);',
' float scan = sin((vUvV.y * 60.0) - uTime * 6.0) * 0.5 + 0.5;',
' scan = pow(scan, 6.0);',
' float flicker = 0.85 + 0.15 * hash(floor(uTime * 14.0));',
' vec3 base = vec3(0.15, 0.75, 0.95);',
' vec3 color = base * (0.35 + fresnel * 1.3 + scan * 0.5) * flicker;',
' float alpha = clamp(fresnel * 0.9 + scan * 0.3 + 0.12, 0.0, 1.0);',
' gl_FragColor = vec4(color, alpha);',
'}',
].join('\n');
const mat = new THREE.ShaderMaterial({
uniforms: { uTime: { value: 0 } },
vertexShader, fragmentShader,
transparent: true, side: THREE.DoubleSide, blending: THREE.AdditiveBlending, depthWrite: false,
});
const mesh = new THREE.Mesh(new THREE.IcosahedronGeometry(1.15, 3), mat);
scene.add(mesh);
const ring = new THREE.Mesh(new THREE.RingGeometry(1.0, 1.3, 48), new THREE.MeshBasicMaterial({ color: 0x2ad8ff, transparent: true, opacity: 0.5, side: THREE.DoubleSide }));
ring.rotation.x = -Math.PI / 2;
ring.position.y = -1.3;
scene.add(ring);
function resize() { renderer.setSize(innerWidth, innerHeight); camera.aspect = innerWidth / innerHeight; camera.updateProjectionMatrix(); }
addEventListener('resize', resize); resize();
renderer.setAnimationLoop((t) => {
const time = t * 0.001;
mat.uniforms.uTime.value = time;
mesh.rotation.y = time * 0.4;
mesh.rotation.x = Math.sin(time * 0.2) * 0.15;
ring.rotation.z = time * 0.3;
renderer.render(scene, camera);
});