Files
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

571 lines
22 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Aurora Borealis - 北极光</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
overflow: hidden;
background: #000;
}
#canvas-container {
width: 100vw;
height: 100vh;
}
#fps {
position: fixed;
top: 10px;
left: 10px;
color: #00ff88;
font-family: 'Courier New', monospace;
font-size: 14px;
text-shadow: 0 0 10px rgba(0, 255, 136, 0.8);
pointer-events: none;
z-index: 100;
}
#info {
position: fixed;
bottom: 10px;
left: 10px;
color: #9d4edd;
font-family: 'Arial', sans-serif;
font-size: 12px;
opacity: 0.7;
pointer-events: none;
}
</style>
</head>
<body>
<div id="fps">FPS: 60</div>
<div id="info">Aurora Borealis - 点击并拖动旋转视角</div>
<div id="canvas-container"></div>
<!-- Three.js CDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
// ============================================
// Aurora Shader - 极光着色器
// ============================================
const AuroraVertexShader = `
varying vec2 vUv;
varying vec3 vPosition;
void main() {
vUv = uv;
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
const AuroraFragmentShader = `
uniform float uTime;
uniform vec2 uResolution;
uniform float uIntensity;
uniform vec3 uColor1;
uniform vec3 uColor2;
uniform float uSpeed;
uniform float uOffset;
varying vec2 vUv;
varying vec3 vPosition;
// 改进的 Perlin Noise 函数
vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec4 permute(vec4 x) { return mod289(((x*34.0)+1.0)*x); }
vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }
float snoise(vec3 v) {
const vec2 C = vec2(1.0/6.0, 1.0/3.0);
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
vec3 i = floor(v + dot(v, C.yyy));
vec3 x0 = v - i + dot(i, C.xxx);
vec3 g = step(x0.yzx, x0.xyz);
vec3 l = 1.0 - g;
vec3 i1 = min(g.xyz, l.zxy);
vec3 i2 = max(g.xyz, l.zxy);
vec3 x1 = x0 - i1 + C.xxx;
vec3 x2 = x0 - i2 + C.yyy;
vec3 x3 = x0 - D.yyy;
i = mod289(i);
vec4 p = permute(permute(permute(
i.z + vec4(0.0, i1.z, i2.z, 1.0))
+ i.y + vec4(0.0, i1.y, i2.y, 1.0))
+ i.x + vec4(0.0, i1.x, i2.x, 1.0));
float n_ = 0.142857142857;
vec3 ns = n_ * D.wyz - D.xzx;
vec4 j = p - 49.0 * floor(p * ns.z * ns.z);
vec4 x_ = floor(j * ns.z);
vec4 y_ = floor(j - 7.0 * x_);
vec4 x = x_ *ns.x + ns.yyyy;
vec4 y = y_ *ns.x + ns.yyyy;
vec4 h = 1.0 - abs(x) - abs(y);
vec4 b0 = vec4(x.xy, y.xy);
vec4 b1 = vec4(x.zw, y.zw);
vec4 s0 = floor(b0)*2.0 + 1.0;
vec4 s1 = floor(b1)*2.0 + 1.0;
vec4 sh = -step(h, vec4(0.0));
vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy;
vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww;
vec3 p0 = vec3(a0.xy, h.x);
vec3 p1 = vec3(a0.zw, h.y);
vec3 p2 = vec3(a1.xy, h.z);
vec3 p3 = vec3(a1.zw, h.w);
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2,p2), dot(p3,p3)));
p0 *= norm.x;
p1 *= norm.y;
p2 *= norm.z;
p3 *= norm.w;
vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
m = m * m;
return 42.0 * dot(m*m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3)));
}
// 多重噪声叠加,增加层次感
float fbm(vec3 p) {
float value = 0.0;
float amplitude = 0.5;
float frequency = 1.0;
for(int i = 0; i < 5; i++) {
value += amplitude * snoise(p * frequency);
amplitude *= 0.5;
frequency *= 2.0;
}
return value;
}
void main() {
vec2 uv = vUv;
float time = uTime * uSpeed;
// 极光带的核心位置(上部天空区域)
float auroraBase = 0.3 + uOffset * 0.3;
// 多层噪声创建流动效果
vec3 noisePos = vec3(uv.x * 3.0 + time * 0.1, uv.y * 2.0, time * 0.15);
float noise1 = fbm(noisePos);
float noise2 = fbm(noisePos * 1.5 + vec3(100.0));
float noise3 = fbm(noisePos * 0.7 + vec3(50.0, time * 0.1, 0.0));
// 极光的垂直分布
float verticalGradient = 1.0 - smoothstep(auroraBase - 0.2, auroraBase + 0.4, uv.y);
verticalGradient *= smoothstep(0.0, auroraBase - 0.1, uv.y);
// 波浪形状
float wave = sin(uv.x * 6.28 + noise1 * 2.0 + time) * 0.15;
wave += sin(uv.x * 12.0 + noise2 * 1.5 + time * 1.3) * 0.08;
wave += noise3 * 0.1;
// 极光带形状
float auroraCenter = auroraBase + wave;
float auroraWidth = 0.15 + noise2 * 0.05;
float auroraShape = 1.0 - smoothstep(0.0, auroraWidth, abs(uv.y - auroraCenter));
auroraShape *= verticalGradient;
// 添加细长的丝带效果
float ribbon = sin(uv.x * 20.0 + time * 2.0 + noise1 * 3.0) * 0.5 + 0.5;
ribbon = pow(ribbon, 4.0) * 0.3;
auroraShape += ribbon * (1.0 - smoothstep(0.0, 0.3, abs(uv.y - auroraCenter)));
// 颜色渐变 - 基于位置和时间
float colorMix = sin(uv.x * 2.0 + time * 0.5 + noise1) * 0.5 + 0.5;
colorMix += noise2 * 0.3;
vec3 color = mix(uColor1, uColor2, colorMix);
// 添加亮度变化
float brightness = 0.6 + noise3 * 0.4;
brightness *= (1.0 + ribbon * 2.0);
// 边缘柔和
float edgeFade = smoothstep(0.0, 0.1, uv.x) * smoothstep(1.0, 0.9, uv.x);
// 最终颜色计算
float alpha = auroraShape * uIntensity * edgeFade * 0.8;
vec3 finalColor = color * brightness * auroraShape * 2.0;
// 添加顶部光晕
float topGlow = exp(-uv.y * 3.0) * noise1 * 0.2;
finalColor += uColor1 * topGlow * uIntensity;
gl_FragColor = vec4(finalColor, alpha);
}
`;
// ============================================
// Star Field Shader - 星空着色器
// ============================================
const StarVertexShader = `
attribute float size;
attribute float opacity;
varying float vOpacity;
void main() {
vOpacity = opacity;
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
gl_PointSize = size * (300.0 / -mvPosition.z);
gl_Position = projectionMatrix * mvPosition;
}
`;
const StarFragmentShader = `
varying float vOpacity;
void main() {
float dist = distance(gl_PointCoord, vec2(0.5));
float alpha = 1.0 - smoothstep(0.0, 0.5, dist);
gl_FragColor = vec4(1.0, 1.0, 1.0, alpha * vOpacity);
}
`;
// ============================================
// Main Application
// ============================================
class AuroraApp {
constructor() {
this.container = document.getElementById('canvas-container');
this.fpsElement = document.getElementById('fps');
this.scene = null;
this.camera = null;
this.renderer = null;
this.auroras = [];
this.stars = null;
this.mountains = null;
this.clock = new THREE.Clock();
this.frameCount = 0;
this.lastTime = 0;
this.mouse = new THREE.Vector2();
this.targetRotation = 0;
this.init();
}
init() {
// 创建场景
this.scene = new THREE.Scene();
// 创建相机
this.camera = new THREE.PerspectiveCamera(
60,
window.innerWidth / window.innerHeight,
0.1,
1000
);
this.camera.position.set(0, 0.5, 4);
this.camera.lookAt(0, 0.3, 0);
// 创建渲染器
this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
this.container.appendChild(this.renderer.domElement);
// 添加背景渐变
this.createBackground();
// 添加星空
this.createStars();
// 添加极光层
this.createAuroras();
// 添加山脉
this.createMountains();
// 事件监听
window.addEventListener('resize', this.onResize.bind(this));
document.addEventListener('mousemove', this.onMouseMove.bind(this));
// 开始动画
this.animate();
}
createBackground() {
// 创建天空渐变背景
const canvas = document.createElement('canvas');
canvas.width = 2;
canvas.height = 512;
const ctx = canvas.getContext('2d');
const gradient = ctx.createLinearGradient(0, 0, 0, 512);
gradient.addColorStop(0.0, '#0a1628'); // 顶部深蓝黑
gradient.addColorStop(0.3, '#0d2137'); // 中上部深绿蓝
gradient.addColorStop(0.6, '#1a3a3a'); // 中部带绿
gradient.addColorStop(0.85, '#0d1f2d'); // 中下部
gradient.addColorStop(1.0, '#000000'); // 底部纯黑
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 2, 512);
const texture = new THREE.CanvasTexture(canvas);
this.scene.background = texture;
}
createStars() {
const starCount = 800;
const positions = new Float32Array(starCount * 3);
const sizes = new Float32Array(starCount);
const opacities = new Float32Array(starCount);
for (let i = 0; i < starCount; i++) {
positions[i * 3] = (Math.random() - 0.5) * 50;
positions[i * 3 + 1] = Math.random() * 25;
positions[i * 3 + 2] = (Math.random() - 0.5) * 30 - 10;
sizes[i] = Math.random() * 2 + 0.5;
opacities[i] = Math.random() * 0.8 + 0.2;
}
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1));
geometry.setAttribute('opacity', new THREE.BufferAttribute(opacities, 1));
const material = new THREE.ShaderMaterial({
uniforms: {},
vertexShader: StarVertexShader,
fragmentShader: StarFragmentShader,
transparent: true,
depthWrite: false,
blending: THREE.AdditiveBlending
});
this.stars = new THREE.Points(geometry, material);
this.scene.add(this.stars);
}
createAuroras() {
// 创建4条极光带,各有不同的参数
const auroraConfigs = [
{
color1: new THREE.Color(0x00ff88), // 绿色
color2: new THREE.Color(0x00ccaa), // 青绿
intensity: 0.7,
speed: 0.3,
offset: 0.0,
yPos: 0.8
},
{
color1: new THREE.Color(0x00ffaa), // 浅绿
color2: new THREE.Color(0x9d4edd), // 紫色
intensity: 0.6,
speed: 0.25,
offset: 0.3,
yPos: 1.0
},
{
color1: new THREE.Color(0x00ff66), // 翠绿
color2: new THREE.Color(0x7b2cbf), // 深紫
intensity: 0.5,
speed: 0.35,
offset: 0.6,
yPos: 1.2
},
{
color1: new THREE.Color(0x88ffaa), // 淡绿
color2: new THREE.Color(0xc77dff), // 淡紫
intensity: 0.4,
speed: 0.2,
offset: 0.9,
yPos: 0.6
}
];
auroraConfigs.forEach((config, index) => {
const geometry = new THREE.PlaneGeometry(12, 4, 64, 32);
const material = new THREE.ShaderMaterial({
uniforms: {
uTime: { value: 0 },
uResolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) },
uIntensity: { value: config.intensity },
uColor1: { value: config.color1 },
uColor2: { value: config.color2 },
uSpeed: { value: config.speed },
uOffset: { value: config.offset }
},
vertexShader: AuroraVertexShader,
fragmentShader: AuroraFragmentShader,
transparent: true,
depthWrite: false,
side: THREE.DoubleSide,
blending: THREE.AdditiveBlending
});
const aurora = new THREE.Mesh(geometry, material);
aurora.position.set(0, config.yPos, -2 - index * 0.5);
aurora.rotation.x = -0.2;
this.auroras.push(aurora);
this.scene.add(aurora);
});
}
createMountains() {
// 创建山脉轮廓
const mountainPoints = [];
const segments = 100;
for (let i = 0; i <= segments; i++) {
const x = (i / segments) * 20 - 10;
// 使用多个正弦波创建自然的山脉轮廓
let y = 0;
y += Math.sin(x * 0.3) * 0.8;
y += Math.sin(x * 0.7 + 1.0) * 0.4;
y += Math.sin(x * 1.2 + 2.0) * 0.2;
y += Math.sin(x * 2.5 + 0.5) * 0.1;
// 添加一些随机变化
y += Math.sin(x * 5.0) * 0.05;
mountainPoints.push(new THREE.Vector2(x, y));
}
// 创建地面
const groundShape = new THREE.Shape();
groundShape.moveTo(-10, -2);
// 添加山脉轮廓
mountainPoints.forEach((point, i) => {
if (i === 0) {
groundShape.moveTo(point.x, point.y);
} else {
groundShape.lineTo(point.x, point.y);
}
});
groundShape.lineTo(10, -2);
groundShape.lineTo(-10, -2);
const geometry = new THREE.ShapeGeometry(groundShape);
const material = new THREE.MeshBasicMaterial({
color: 0x000000,
side: THREE.DoubleSide
});
this.mountains = new THREE.Mesh(geometry, material);
this.mountains.position.z = -1;
this.scene.add(this.mountains);
// 添加第二层远山(较淡)
const farMountainPoints = [];
for (let i = 0; i <= segments; i++) {
const x = (i / segments) * 25 - 12.5;
let y = Math.sin(x * 0.2) * 1.2 + Math.sin(x * 0.5) * 0.6;
y += 1.5;
farMountainPoints.push(new THREE.Vector2(x, y));
}
const farShape = new THREE.Shape();
farMountainPoints.forEach((point, i) => {
if (i === 0) farShape.moveTo(point.x, point.y);
else farShape.lineTo(point.x, point.y);
});
farShape.lineTo(15, -2);
farShape.lineTo(-15, -2);
const farGeometry = new THREE.ShapeGeometry(farShape);
const farMaterial = new THREE.MeshBasicMaterial({
color: 0x05101a,
side: THREE.DoubleSide
});
const farMountains = new THREE.Mesh(farGeometry, farMaterial);
farMountains.position.z = -3;
this.scene.add(farMountains);
}
onResize() {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
// 更新uniforms
this.auroras.forEach(aurora => {
aurora.material.uniforms.uResolution.value.set(
window.innerWidth,
window.innerHeight
);
});
}
onMouseMove(event) {
this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
this.targetRotation = this.mouse.x * 0.1;
}
updateFPS() {
this.frameCount++;
const currentTime = performance.now();
if (currentTime - this.lastTime >= 1000) {
const fps = this.frameCount;
this.fpsElement.textContent = `FPS: ${fps}`;
this.frameCount = 0;
this.lastTime = currentTime;
}
}
animate() {
requestAnimationFrame(this.animate.bind(this));
const elapsedTime = this.clock.getElapsedTime();
// 更新极光
this.auroras.forEach((aurora, index) => {
aurora.material.uniforms.uTime.value = elapsedTime;
// 轻微的摆动
aurora.position.x = Math.sin(elapsedTime * 0.1 + index) * 0.2;
});
// 星星闪烁
if (this.stars) {
this.stars.rotation.y = elapsedTime * 0.005;
}
// 相机缓慢移动
this.camera.position.x += (this.targetRotation - this.camera.position.x) * 0.02;
this.camera.lookAt(0, 0.5, 0);
// 更新FPS
this.updateFPS();
// 渲染
this.renderer.render(this.scene, this.camera);
}
}
// 启动应用
window.addEventListener('load', () => {
new AuroraApp();
});
</script>
</body>
</html>