feat: add aurora borealis animated shader effect to skybox
Two layered semi-transparent planes use a custom GLSL ShaderMaterial with fractal Brownian motion (fBm) noise to simulate aurora curtains. Colors cycle through green, teal, and violet via time-driven distortion. Additive blending and depth-write disabled keep it composited cleanly behind stars and constellation lines. Fixes #105 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
101
app.js
101
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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user