threejs宇宙尺度:从量子到宇宙的62个数量级
基于 Three.js 构建的宇宙刻度可视化页面:一根对数刻度轴横跨 10^-35 米(普朗克长度)到 10^27 米(可观测宇宙),滚轮即可在 62 个数量级、95 个刻度项之间连续穿行,每种尺度都有程序化生成的专属 3D 形态。
尺度可视化 (Universe Scale)
上一篇的「宇宙层级之旅」是离散的 11 个层级,这一篇是它的极限版:连续的对数刻度轴。从普朗克长度到可观测宇宙约 62 个数量级,中间排布了 95 个真实或假想的尺度对象——量子泡沫、夸克、DNA、病毒、细胞、珠峰、太阳系、星系团、宇宙丝状结构……滚轮一滚,镜头就沿着刻度轴滑行,到达某个刻度时,对应的 3D 形态会淡入展开。
目录结构
1 | |
数据模型 (scaleData.ts)
每个刻度项是一个 ScaleItem:
| 字段 | 含义 |
|---|---|
size |
实际尺寸(米),如普朗克长度 1.616e-35 |
exponent |
10 的指数(-35 ~ 27),用于刻度轴定位 |
shape |
形态类型,共 22 种(见下文) |
color |
主题色,随数量级渐变:量子尺度偏紫红,日常尺度偏绿,宇宙尺度偏青 |
数据按物理量真实排布:夸克 < 10^-18 m,电子 ~2.8×10^-15 m,DNA 直径 ~2 nm,病毒 ~100 nm,红细胞 ~7 μm,珠峰 ~8.8 km,太阳 ~1.39×10^6 km,本星系群 ~10^23 m……约 62 个数量级的跨度在一条对数轴上被完整还原。
核心机制:对数刻度轴
状态与插值
页面只有两个核心状态:
1 | |
滚轮和触屏捏合只改 targetLogPosition,动画循环里把 logPosition 平滑地插值逼近目标值(每帧按比例收敛),形成”顺滑穿行”的手感。位置变化时自动匹配最近的刻度项,切换其 3D 形态。
与 OrbitControls 的配合
一个容易踩的坑:滚轮默认会被 OrbitControls 拿去做相机缩放。这里刻意 controls.enableZoom = false,把滚轮和双指捏合完全接管用于尺度切换,OrbitControls 只负责旋转和平移——否则”缩放视角”和”切换尺度”两个语义会打架。
22 种程序化形态
所有 3D 形态都是程序化生成的,没有任何外部模型。按尺度从小到大:
| 形态 | 对应尺度 | 实现要点 |
|---|---|---|
quantum-foam |
普朗克长度、弦 | 二十面体概率云核心 + 300 个紫-青渐变涨落粒子 |
particle |
中微子、夸克、电子 | 小型发光球体 |
nucleus |
原子核、质子 | 多球簇状组合 |
atom |
原子 | 核心 + 轨道电子 |
molecule |
分子、水分子 | 多球连接结构 |
helix |
DNA、蛋白质 | 双螺旋曲线 |
virus |
病毒 | 带刺突的球体 |
cell |
细胞 | 有机形态球体 |
organism |
蚂蚁、人 | 椭球体 |
object |
硬币、书 | 立方体/长方体 |
building |
楼房、埃菲尔铁塔 | 高耸柱体 |
mountain |
珠峰 | 锥体 |
orbit |
近地轨道 | 球体 + 环 |
planet |
地球、木星 | 带纹理球体 |
star |
太阳、蓝巨星 | 发光球体 + 光晕 |
solar-system |
太阳系 | 中心恒星 + 轨道行星 |
nebula |
猎户座星云 | 粒子云团 |
galaxy |
银河系 | 螺旋粒子盘 |
galaxy-cluster |
星系团 | 多球分布 |
filament |
宇宙丝状结构 | 丝线网络 |
cosmic-void |
宇宙空洞 | 边界球 |
universe |
可观测宇宙 | 边界球 + 粒子 |
每种形态由 createShapeObject(shape, color) 按主题色生成,装入 shapeGroup;切换刻度项时清空重建,几何体和材质全部 dispose,保证长时间滑动不泄漏显存。
交互设计
尺度切换
| 操作 | 桌面端 | 移动端 |
|---|---|---|
| 切换尺度 | 滚轮 | 双指捏合 / 上下滑动 |
| 旋转视角 | 左键拖动 | 单指滑动 |
| 平移视角 | 右键拖动 | 双指滑动 |
刻度轴 UI
- 顶部/底部对数刻度轴:实时显示当前在对数轴上的位置(0-100%)与当前项的指数(如
10⁻¹⁵ m) - 尺寸格式化:根据量级自动换算单位——飞米、皮米、纳米、微米、毫米、米、公里、天文单位、光年,导航菜单里还有短格式(
fm/nm/AU/ly) - 分组导航:95 个刻度项按四个分组折叠
- ⚛️ 量子尺度(< 10^-15 m)
- 🔬 微观世界(10^-15 ~ 10^-2 m)
- 📏 日常尺度(10^-2 ~ 10^7 m)
- 🌌 天文尺度(> 10^7 m)
- 点击任意刻度项即可跳转,镜头沿对数轴滑行到目标
过渡动画
形态切换时新旧对象做透明度/缩放过渡,同时相机沿视线方向平滑过渡到目标距离(desiredCameraDistance),不同尺度的物体”远近感”由此统一:看原子和看星系团的构图体验是一致的。
性能要点
- 按需构建:22 种形态只在对应刻度项激活时才生成,离开即销毁
- 资源回收:
clearShapeGroup()对 Mesh / Points / Line 三类对象分别做几何体与材质的dispose - 像素比上限:
renderer.setPixelRatio(Math.min(devicePixelRatio, 2)),避免高分屏 4x 填充率开销 - 插值收敛:刻度位置用帧率无关的比例逼近,不依赖固定步长,不同刷新率的屏幕手感一致
Three.js 特性与实现
相机与控制器的”重新分工”
本模块最有意思的 Three.js 用法是禁用了它的部分能力:
OrbitControls.enableZoom = false——滚轮被页面原生wheel监听器拦截,专门用于沿对数刻度轴滑行;touchmove同理被接管为捏合切尺度- OrbitControls 只保留旋转和平移(
rotateSpeed: 0.5、panSpeed: 0.5),并保留阻尼 - 视角”远近感”不靠用户缩放,而是每个形态自带
desiredCameraDistance,切换时相机沿当前视线方向(_camDir单位向量)平滑推进/后退到位——看原子和看星系团的构图体验因此完全一致
这本质上是把”缩放”这个自由度从相机转移给了世界本身:用户以为在放大物体,实际是在不同尺度间穿行。
程序化几何(Geometry 组合拳)
22 种形态全部由 Three.js 基础几何体组合生成,没有外部模型:
- 量子泡沫:
IcosahedronGeometry(二十面体,细分 4 级)做概率云核心 + 300 粒子的BufferGeometry涨落云,顶点颜色用THREE.Color().setHSL()在紫-青色域随机采样 - 原子:核心
SphereGeometry+ 电子轨道椭圆参数方程逐帧算位置 - DNA:双螺旋是两条相位差 π 的正弦/余弦曲线点列 + 连接横档
- 病毒:主体球 + 表面按斐波那契分布的刺突小球
- 星系:粒子盘按对数螺旋分布,与层级之旅那篇的银河系同源但参数不同
所有用色都从当前刻度项的主题色 color.getHSL() 出发微调明度/饱和,保证 62 个数量级穿行时画面色调是连续渐变的。
材质选择
- 发光体(粒子、电子、恒星):
MeshStandardMaterial/MeshPhongMaterial开emissive自发光 - 粒子云:
PointsMaterial+vertexColors+AdditiveBlending,透明叠加出星云质感 - 不受光的容器/边界:
MeshBasicMaterial低透明度
帧循环与状态管理
每帧做三件事:
logPosition += (targetLogPosition - logPosition) * k指数收敛——帧率无关的平滑插值,不依赖固定步长- 根据新位置匹配最近刻度项,若变化则重建
shapeGroup并启动过渡动画(transitionProgress0→1 驱动透明度和缩放) - 相机距离向
desiredCameraDistance收敛 +controls.update()+ 渲染
资源管理
clearShapeGroup()对shapeGroup里的Mesh/Points/Line三类对象分别geometry.dispose()和material.dispose()(注意材质可能是数组,要遍历)——每次滚轮跨过刻度项都会触发重建,不做回收显存会迅速爆掉renderer.setPixelRatio(Math.min(devicePixelRatio, 2))封顶像素比,4K 屏省一半以上填充率
技术栈
- Vue 3: 组件框架(Composition API)
- Three.js: 3D 渲染引擎
- TypeScript: 类型安全
- OrbitControls: 轨道控制器(禁用缩放,滚轮让给尺度轴)