diff --git a/app.js b/app.js index 14b95c0..bc2e4f2 100644 --- a/app.js +++ b/app.js @@ -116,6 +116,103 @@ function buildConstellationLines() { const constellationLines = buildConstellationLines(); scene.add(constellationLines); +// === AURORA BOREALIS === +const auroraVertexShader = ` + varying vec2 vUv; + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); + } +`; + +const auroraFragmentShader = ` + uniform float uTime; + varying vec2 vUv; + + float hash(vec2 p) { + return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); + } + + float noise(vec2 p) { + vec2 i = floor(p); + vec2 f = fract(p); + float a = hash(i); + float b = hash(i + vec2(1.0, 0.0)); + float c = hash(i + vec2(0.0, 1.0)); + float d = hash(i + vec2(1.0, 1.0)); + vec2 u = f * f * (3.0 - 2.0 * f); + return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y; + } + + float fbm(vec2 p) { + float v = 0.0; + float amp = 0.5; + for (int i = 0; i < 5; i++) { + v += amp * noise(p); + p *= 2.0; + amp *= 0.5; + } + return v; + } + + void main() { + vec2 uv = vUv; + float t = uTime * 0.12; + + // Distort UVs for flowing curtain motion + float dx = fbm(vec2(uv.x * 2.5 + t * 0.35, uv.y * 1.5 + t * 0.2)) * 0.28; + float dy = fbm(vec2(uv.x * 2.5 - t * 0.28, uv.y * 1.5 + t * 0.18)) * 0.18; + vec2 dUv = uv + vec2(dx, dy); + + // Vertical curtain bands — shift along x over time + float band1 = fbm(vec2(dUv.x * 3.2 + t * 0.55, t * 0.3)); + float band2 = fbm(vec2(dUv.x * 5.1 - t * 0.42, t * 0.22)); + float bands = band1 * 0.65 + band2 * 0.35; + + // Vertical envelope: bright in the upper portion, fade at edges + float yFade = smoothstep(0.0, 0.22, uv.y) * smoothstep(1.0, 0.45, uv.y); + + float intensity = pow(bands * yFade, 1.6); + + // Color palette: green <-> teal <-> violet + vec3 green = vec3(0.0, 1.0, 0.45); + vec3 teal = vec3(0.0, 0.78, 1.0); + vec3 violet = vec3(0.55, 0.1, 1.0); + + float mix1 = fbm(vec2(dUv.x * 2.2 + t * 0.18, uv.y * 3.0)); + float mix2 = fbm(vec2(uv.x * 4.0 + t * 0.3, uv.y * 2.0 - t * 0.12)) * 0.55; + vec3 color = mix(green, teal, mix1); + color = mix(color, violet, mix2); + + float alpha = intensity * 0.6; + gl_FragColor = vec4(color * intensity, alpha); + } +`; + +const auroraMaterial = new THREE.ShaderMaterial({ + uniforms: { uTime: { value: 0.0 } }, + vertexShader: auroraVertexShader, + fragmentShader: auroraFragmentShader, + transparent: true, + blending: THREE.AdditiveBlending, + depthWrite: false, + side: THREE.DoubleSide, +}); + +// Two overlapping planes for depth — slightly different positions/rotations +const auroraGeo = new THREE.PlaneGeometry(700, 220, 1, 1); +const aurora = new THREE.Mesh(auroraGeo, auroraMaterial); +aurora.position.set(0, 75, -130); +aurora.rotation.x = -0.25; +scene.add(aurora); + +const auroraMaterial2 = auroraMaterial.clone(); +auroraMaterial2.uniforms = { uTime: { value: 0.0 } }; +const aurora2 = new THREE.Mesh(new THREE.PlaneGeometry(700, 220, 1, 1), auroraMaterial2); +aurora2.position.set(0, 90, -160); +aurora2.rotation.x = -0.18; +scene.add(aurora2); + // === MOUSE-DRIVEN ROTATION === let mouseX = 0; let mouseY = 0; @@ -252,6 +349,10 @@ function animate() { constellationLines.rotation.x = stars.rotation.x; constellationLines.rotation.y = stars.rotation.y; + // Advance aurora shader time + auroraMaterial.uniforms.uTime.value = elapsed; + auroraMaterial2.uniforms.uTime.value = elapsed + 8.3; // offset for variety + // Subtle pulse on constellation opacity constellationLines.material.opacity = 0.12 + Math.sin(elapsed * 0.5) * 0.06;