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(0x0a0620);
scene.fog = new THREE.Fog(0x0a0620, 3, 11);
const camera = new THREE.PerspectiveCamera(55, 1, 0.1, 60);
camera.position.set(0, 0.9, 3.6);
camera.rotation.x = -0.18;
const sunVertex = [
'varying vec2 vUv;',
'void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }',
].join('\n');
const sunFragment = [
'varying vec2 vUv;',
'uniform float uTime;',
'void main() {',
' vec2 p = vUv - 0.5;',
' float d = length(p) * 2.0;',
' if (d > 1.0) discard;',
' float stripe = step(0.5, fract((vUv.y - uTime * 0.05) * 10.0));',
' vec3 top = vec3(1.0, 0.78, 0.35);',
' vec3 bottom = vec3(1.0, 0.25, 0.55);',
' vec3 color = mix(bottom, top, vUv.y);',
' float mask = vUv.y > 0.46 ? 1.0 : stripe;',
' gl_FragColor = vec4(color, mask);',
'}',
].join('\n');
const sunMat = new THREE.ShaderMaterial({
uniforms: { uTime: { value: 0 } },
vertexShader: sunVertex,
fragmentShader: sunFragment,
transparent: true,
side: THREE.DoubleSide,
});
const sun = new THREE.Mesh(new THREE.PlaneGeometry(3.2, 3.2), sunMat);
sun.position.set(0, 0.9, -8);
scene.add(sun);
const gridVertex = [
'varying vec2 vUv;',
'void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }',
].join('\n');
const gridFragment = [
'varying vec2 vUv;',
'uniform float uOffset;',
'void main() {',
' vec2 p = vUv * vec2(24.0, 40.0);',
' p.y += uOffset;',
' vec2 g = abs(fract(p - 0.5) - 0.5) / fwidth(p);',
' float line = min(g.x, g.y);',
' float fade = smoothstep(1.0, 0.05, vUv.y);',
' vec3 color = mix(vec3(1.0, 0.25, 0.85), vec3(0.25, 0.85, 1.0), vUv.x);',
' float alpha = (1.0 - min(line, 1.0)) * fade;',
' gl_FragColor = vec4(color, alpha);',
'}',
].join('\n');
const gridMat = new THREE.ShaderMaterial({
uniforms: { uOffset: { value: 0 } },
vertexShader: gridVertex,
fragmentShader: gridFragment,
transparent: true,
side: THREE.DoubleSide,
});
const grid = new THREE.Mesh(new THREE.PlaneGeometry(14, 20, 1, 1), gridMat);
grid.rotation.x = -Math.PI / 2;
grid.position.y = -0.5;
scene.add(grid);
function resize() { renderer.setSize(innerWidth, innerHeight); camera.aspect = innerWidth / innerHeight; camera.updateProjectionMatrix(); }
addEventListener('resize', resize); resize();
renderer.setAnimationLoop((t) => {
const time = t * 0.001;
gridMat.uniforms.uOffset.value = time * 1.4;
sunMat.uniforms.uTime.value = time;
renderer.render(scene, camera);
});