如何用 three.js 和真实的星历把音乐网站做成一座可运转的太阳系仪
原作者: davidlilja
原文链接: https://discourse.threejs.org/t/i-built-my-music-site-as-a-working-orrery-three-js-real-ephemeris/93649

项目概览
原文展示的是一个器乐电子音乐网站 Árstraumur · Instrumental electronic music,其核心创意是将整站做成一座可交互、可运转的太阳系仪(Orrery)。网站描述为“Instrumental electronic music shaped by atmosphere, not genre…”,强调氛围驱动而非流派定义。技术栈以 three.js 为主体,并引入 真实星历(real ephemeris) 数据,让行星位置并非简单循环动画,而是基于真实天文计算。
原文为 Discourse 帖子的精简摘要,未包含完整代码块;下文的“核心代码参考”部分是基于该类型实现(three.js + 真实星历 + Orrery)梳理出的典型关键片段,方便理解其技术内核。
核心代码参考
1. 场景初始化与行星网格创建
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
45,
innerWidth / innerHeight,
0.1,
1000,
);
camera.position.set(0, 20, 40);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(devicePixelRatio);
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
// 加载真实星历数据,返回行星位置查询器
const ephemeris = await loadEphemeris(); // 例如解析 JPL DE440 或 Kepler 元素
const planets = createPlanetMeshes(scene); // 根据配置生成球体网格
2. 基于真实星历更新行星位置
function animate(time) {
const t = time * 0.001; // 秒 -> 时间戳,按需缩放时间
for (const planet of planets) {
// 从星历获取当前行星的日心/地心坐标
const position = ephemeris.getPosition(planet.name, t);
planet.mesh.position.set(position.x, position.y, position.z);
}
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
3. 可选:GPU 端着色器优化轨道计算
若将部分计算移至着色器,可在顶点着色器中解析轨道参数:
// vertex shader 片段
uniform float uTime;
attribute vec3 aKepler; // 半长轴、离心率、倾角等参数
void main() {
vec3 pos = computeOrbitalPosition(aKepler, uTime);
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
说明:以上代码为技术推演示例,原文未提供可直接复制粘贴的代码块,但这是该类型项目常见的实现骨架。
三大核心优化要点
-
按需加载天文数据与纹理资源
真实星历数据通常体积较大,不宜一次性全部加载。应使用异步加载、分块解析,并在行星进入视口或交互聚焦时才加载高精度纹理。这样可以显著降低首屏初始化时间,避免卡顿。 -
减少绘制调用:合并几何体与使用 Instancing
Orrery 中可能存在大量小行星、轨道环、粒子等。若每个轨道环单独一个 draw call,帧率会迅速下降。推荐使用THREE.InstancedMesh渲染同类型对象,或将静态几何体合并为单个 BufferGeometry,从而将 draw call 从数百次降低到个位数。 -
利用 LOD 与着色器分担 CPU 计算压力
真实星历计算如果全部在 JavaScript 主循环中逐帧执行,会占用大量 CPU 时间。可以采用 细节层次(Level of Detail):远距离行星使用简化轨道模型,近距离时再切换真实星历;同时将重复性轨道参数计算转移到 GLSL 着色器,让 GPU 并行处理,保持主线程流畅。
GLSL / WebGL 专业术语检查
以下术语在解读与实现中高频出现,确保中文表达准确:
| 英文术语 | 推荐中文翻译 | 说明 |
|---|---|---|
| Orrery | 太阳系仪 | 用于演示行星相对运动的机械或数字模型 |
| Ephemeris | 星历 | 天体在特定时间的位置数据表或计算函数 |
| GLSL | GLSL | OpenGL Shading Language,保留缩写 |
| Shader | 着色器 | 运行在 GPU 上的程序,分为顶点/片元着色器 |
| Draw call | 绘制调用 | CPU 向 GPU 发起的单次渲染指令 |
| Instancing | 实例化 | 一次绘制多个相同几何体,降低 draw call |
| Level of Detail (LOD) | 细节层次 | 根据距离或屏幕占比切换模型复杂度 |
| Frame rate | 帧率 | 每秒渲染帧数,衡量流畅度 |
| Texture atlas | 纹理图集 | 将多张纹理合并到一张,减少纹理切换 |
| Pixel ratio | 像素比 | 物理像素与 CSS 像素的比值,影响清晰度 |
| Antialiasing | 抗锯齿 | 减少边缘锯齿,提升视觉质量 |
| Kepler elements | 开普勒轨道要素 | 描述轨道形状、方向和时间的一组参数 |
| Heliocentric | 日心的 | 以太阳为中心的坐标系 |
| Geocentric | 地心的 | 以地球为中心的坐标系 |
结语
该项目将音乐网站与天文可视化巧妙结合,不仅展示了 three.js 在创意交互中的潜力,也体现了真实科学数据(星历)在 Web3D 中的应用价值。通过合理的资源管理、绘制调用优化和 GPU 并行计算,可以保证这种动态天体场景在浏览器中依然流畅运行。若你正计划类似项目,以上三个优化方向会是最值得优先投入的工程重点。
