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

170 lines
7.4 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>09 - LORENZ | 洛伦兹吸引子</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #000; overflow: hidden; font-family: 'Space Mono', monospace; }
#canvas-container { width: 100vw; height: 100vh; }
.info {
position: fixed; top: 20px; left: 20px; color: #ff3366;
font-size: 14px; pointer-events: none; z-index: 100;
text-shadow: 0 0 10px rgba(255,51,102,0.8);
}
.info h1 { font-size: 20px; margin-bottom: 8px; }
.info p { opacity: 0.8; font-size: 12px; margin: 4px 0; }
.formula {
position: fixed; top: 20px; right: 20px; color: #888;
font-size: 11px; text-align: right; font-family: 'Courier New', monospace;
background: rgba(0,0,0,0.8); padding: 15px; border-radius: 8px;
border: 1px solid #333;
}
.formula code { color: #ff3366; display: block; margin: 3px 0; }
</style>
</head>
<body>
<div id="canvas-container"></div>
<div class="info">
<h1>09 - LORENZ</h1>
<p>洛伦兹吸引子 - 蝴蝶效应</p>
<p>σ=10, ρ=28, β=8/3</p>
<p>粒子数: <span id="count">0</span></p>
<p>FPS: <span id="fps">0</span></p>
</div>
<div class="formula">
<div>微分方程组:</div>
<code>dx/dt = σ(y - x)</code>
<code>dy/dt = x(ρ - z) - y</code>
<code>dz/dt = xy - βz</code>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/EffectComposer.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/RenderPass.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/ShaderPass.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/shaders/CopyShader.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/shaders/LuminosityHighPassShader.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/UnrealBloomPass.js"></script>
<script>
const COUNT = 6000;
const sigma = 10, rho = 28, beta = 8/3;
const scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x000000, 0.003);
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 2000);
camera.position.set(60, 40, 60);
const renderer = new THREE.WebGLRenderer({ antialias: true, powerPreference: "high-performance" });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.getElementById('canvas-container').appendChild(renderer.domElement);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.autoRotate = true;
controls.autoRotateSpeed = 0.3;
const renderScene = new THREE.RenderPass(scene, camera);
const bloomPass = new THREE.UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0);
bloomPass.strength = 2.0; bloomPass.radius = 0.6; bloomPass.threshold = 0;
const composer = new THREE.EffectComposer(renderer);
composer.addPass(renderScene);
composer.addPass(bloomPass);
const dummy = new THREE.Object3D();
const color = new THREE.Color();
const geometry = new THREE.SphereGeometry(0.25, 8, 8);
const material = new THREE.MeshBasicMaterial({ color: 0xffffff });
const instancedMesh = new THREE.InstancedMesh(geometry, material, COUNT);
instancedMesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
scene.add(instancedMesh);
// 洛伦兹轨迹点
const trajectories = [];
const trailLength = 50;
for (let i = 0; i < COUNT; i++) {
trajectories.push({
x: (Math.random() - 0.5) * 20,
y: (Math.random() - 0.5) * 20 + 20,
z: (Math.random() - 0.5) * 20 + 25,
trail: [],
phase: i / COUNT * Math.PI * 2
});
}
document.getElementById('count').textContent = COUNT;
let lastTime = performance.now(), frameCount = 0;
const fpsEl = document.getElementById('fps');
const clock = new THREE.Clock();
function lorenzStep(x, y, z, dt) {
const dx = sigma * (y - x) * dt;
const dy = (x * (rho - z) - y) * dt;
const dz = (x * y - beta * z) * dt;
return { x: x + dx, y: y + dy, z: z + dz };
}
function animate() {
requestAnimationFrame(animate);
const now = performance.now();
frameCount++;
if (now - lastTime >= 1000) { fpsEl.textContent = frameCount; frameCount = 0; lastTime = now; }
const time = clock.getElapsedTime();
const dt = 0.008;
for (let i = 0; i < COUNT; i++) {
const t = trajectories[i];
// 洛伦兹迭代
const next = lorenzStep(t.x, t.y, t.z, dt);
t.x = next.x; t.y = next.y; t.z = next.z;
// 添加扰动防止全部收敛
if (Math.random() < 0.001) {
t.x += (Math.random() - 0.5) * 2;
t.y += (Math.random() - 0.5) * 2;
}
// 更新轨迹
t.trail.push({x: t.x, y: t.y, z: t.z});
if (t.trail.length > trailLength) t.trail.shift();
// 在轨迹上显示(制造拖尾效果)
const trailPos = t.trail[Math.floor((Math.sin(time * 2 + t.phase) + 1) / 2 * (t.trail.length - 1))] || t.trail[0];
dummy.position.set(trailPos.x, trailPos.y, trailPos.z);
dummy.scale.setScalar(0.8 + Math.sin(time * 3 + i * 0.01) * 0.3);
dummy.updateMatrix();
instancedMesh.setMatrixAt(i, dummy.matrix);
// 颜色:基于速度和位置
const speed = Math.sqrt(Math.pow(next.x - t.x, 2) + Math.pow(next.y - t.y, 2)) / dt;
const hue = 0.95 + (t.y - 20) / 40 * 0.1; // 粉红到紫色
const lightness = 0.4 + Math.min(speed / 50, 0.4);
color.setHSL(hue, 0.9, lightness);
instancedMesh.setColorAt(i, color);
}
instancedMesh.instanceMatrix.needsUpdate = true;
instancedMesh.instanceColor.needsUpdate = true;
controls.update();
composer.render();
}
animate();
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
composer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>