threejs宇宙层级之旅:从地球到可观测宇宙

基于 Three.js 构建的宇宙层级可视化页面,用 11 个独立的 3D 场景串起从地球到全宇宙的尺度序列,支持中英文切换与键盘导航。

宇宙层级之旅 (Cosmos Dimensions)

上一篇文章介绍了工程中的太阳系模拟,这一篇介绍同工程的「宇宙层级」模块。它回答一个问题:如果把视野不断放大,从脚下这颗行星一路拉到可观测宇宙之外,会依次看到什么?

目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
src/pages/cosmos/
├── App.vue # 主页面组件
├── useThree.ts # Three.js 生命周期封装(Composable)
├── scenes/
│ └── index.ts # 11 个层级场景的实现
├── data/
│ └── levels.ts # 层级数据配置
├── components/
│ ├── CosmosViewer.vue # 3D 视图容器 + 场景切换
│ ├── LevelNav.vue # 底部层级导航条
│ └── InfoPanel.vue # 层级信息面板
└── locales/
├── zh.ts # 中文翻译
└── en.ts # 英文翻译

架构说明

1. 层级数据 (levels.ts)

用一份 LevelInfo 数组描述整个尺度序列,每个层级包含:

  • 中英文名称 与量级说明(如 “直径 ~930 亿光年”)
  • 科学描述 与关键数据(用于信息面板展示)
  • 主题色:驱动导航圆点、信息面板等 UI 的配色

2. 场景封装 (useThree.ts)

一个轻量的 Vue Composable,把 Three.js 的样板代码收敛成可复用的能力:

  • 初始化:透视相机、WebGL 渲染器、带阻尼的轨道控制器
  • 动画循环:requestAnimationFrame 驱动,每帧调用当前场景注册的 update 回调
  • 场景清理:切换层级时递归移除场景对象并 dispose 几何体与材质,避免显存泄漏
  • 自适应:监听窗口变化,同步相机宽高比与渲染尺寸

3. 场景工厂 (scenes/index.ts)

每个层级对应一个 createXxxScene(scene, camera) 工厂函数:往场景里添加对象、摆好相机位置,返回一个每帧执行的动画函数。CosmosViewer 持有工厂函数数组,切换层级时 clearScene() 后重新加载。

4. 视图容器 (CosmosViewer.vue)

负责层级间的过渡:切换时先让遮罩层淡出(400ms),在遮罩完全不透明时替换场景,再淡入新场景,避免生硬跳变。

11 个层级

# 层级 尺度 场景亮点
0 地球 直径 ~12,742 km 真实纹理 + 双层大气光晕 + 云层
1 地月系 地月距离 ~38.4 万 km 月球公转动画 + 引力连线实时跟随
2 太阳系 奥尔特云 ~2 光年 八大行星轨道 + 土星环 + 太阳多层光晕
3 银河系 直径 ~10 万光年 5 万粒子旋臂 + 银心辉光 + 太阳位置标记
4 本星系群 直径 ~1000 万光年 银河系/仙女座/麦哲伦云 + 星系间引力连线
5 室女座超星系团 直径 ~1.1 亿光年 200 个星系 + 本星系群位置标记
6 拉尼亚凯亚超星系团 直径 ~5.2 亿光年 巨引源脉动 + 12 条丝状结构
7 双鱼-鲸鱼座复合体 长约 10 亿光年 星系长城丝状结构
8 宇宙大尺度丝状结构 百亿光年级别 40 条随机游走丝线 + 宇宙空洞
9 可观测宇宙 直径 ~930 亿光年 边界球 + 8000 星系粒子 + 中心观测者
10 全宇宙 无限 / 未知 多层渐隐球体模拟无限延伸

几个实现上比较有代表性的场景:

银河系:粒子旋臂

5 万个粒子按对数螺旋分布——每个粒子先随机归属四条旋臂之一,再根据到银心的距离 dist 计算偏转角 armAngle + dist * 0.06,叠加随机散布形成旋臂与核球。颜色上 30% 的粒子偏蓝(模拟年轻恒星),其余偏黄白,银心用半透明球体叠加出辉光。太阳的位置用黄色标记点 + 脉冲光环标出,直观展示”太阳在猎户座旋臂、距银心 2.6 万光年”。

宇宙网:随机游走丝线

40 条丝状结构用随机游走(random walk)生成:从随机起点出发,每步在三个方向上随机偏移并连线,天然形成”细长弯曲”的丝状形态;再叠加 200 个星系光点与 15 个暗色”空洞”球体,还原宇宙大尺度结构的骨架形态。

全宇宙:无限感

没有边界可画,就用 10 层半径递增、不透明度极低的球壳制造纵深渐隐,配合 1.5 万个亮度随距离衰减的星系粒子(brightness = 0.8 - dist * 0.7),让画面呈现”向无限延伸”的视觉暗示;中心保留一个小小的可观测宇宙光点,暗示我们所在的位置。

交互设计

导航方式

  • 底部导航条:11 个层级圆点 + 名称,毛玻璃背景,移动端可横向滚动
  • 左右箭头:底部中央的「缩小 / 放大」按钮
  • 键盘:← →(以及上下方向键)直接切换层级
  • 箭头按钮在首尾层级自动禁用

场景切换

每个层级都是独立场景,切换采用”清空 + 重建”策略:

1
fade-out (400ms) → clearScene() → loadScene() → fade-in

配合 useThree 的资源回收,保证反复切换不会累积 GPU 资源。

信息面板

点击导航或切层级后,侧边信息面板展示当前层级的中英文名称、尺度、科学描述和关键数据,主题色随层级联动。

技术要点

  • Vue Composable 封装 Three.js:useThree 把初始化、销毁、resize、动画循环全部收敛,场景代码只关注”建什么”和”怎么动”
  • 工厂函数 + 更新回调:每个场景返回闭包形式的 update 函数,主循环只调用它,职责单一
  • 显存管理:切换场景时遍历 scene.children,逐个 geometry.dispose() / material.dispose()
  • 数据驱动 UI:层级名称、描述、主题色全部来自 levels.ts,新增层级只需加一份数据 + 一个工厂函数

Three.js 特性与实现

渲染基座(WebGLRenderer / PerspectiveCamera / OrbitControls)

  • WebGLRenderer:antialias: true 抗锯齿,alpha: true 透明背景让页面 CSS 背景透出,setPixelRatio(devicePixelRatio) 保证高分屏清晰度
  • PerspectiveCamera:远裁剪面开到 100000——因为”全宇宙”层级里最远的粒子在 800 单位外、近处天体只有几个单位,跨度大,必须放宽深度范围
  • OrbitControls:enableDamping + dampingFactor: 0.05 开启阻尼,拖拽松手后视角带惯性缓停,手感比生硬跟随好很多;每帧必须调用 controls.update() 驱动阻尼

粒子与几何体

  • BufferGeometry + BufferAttribute:所有星场(每层 2000~15000 粒子)都是手写 Float32Array 填位置和颜色,再 setAttribute('position'/'color'),配合 PointsMaterial({ vertexColors: true }) 实现单次 draw call 渲染成千上万颗不同亮度的星
  • 银河系旋臂:5 万粒子按对数螺旋公式布点,颜色上 30% 偏蓝模拟年轻恒星——纯数学生成,零模型文件
  • 几何体多样性:SphereGeometry(天体/星系光晕)、RingGeometry(月球轨道盘、太阳脉冲环)、Line + setFromPoints(轨道虚线、引力连线、丝状结构),基本覆盖了”点、线、面”三类基本图元

材质策略:两类材质各司其职

  • MeshPhongMaterial:用在需要光照的实心天体(地球、行星),地球还开了 specular 高光模拟海面反光
  • MeshBasicMaterial:用在自发光体(太阳、星系光球、标记点)——不受光源影响,配 transparent + opacity 就能做出辉光
  • BackSide 光晕技巧:所有”光晕”都是一个比本体大的球,材质设 side: THREE.BackSide 只渲染背面,套在本体外面形成柔和的发光边缘,比后期泛光(bloom)便宜得多
  • LineDashedMaterial:轨道和引力连线用虚线,调用 computeLineDistances() 才能生效(容易漏的坑)

场景切换与资源回收

层级切换是”清空重建”而非”预建多个场景切 visible”,这里用到一个必须做对的流程:

1
遍历 scene.children → remove → geometry.dispose() → material.dispose()

不 dispose 的话,GPU 端的缓冲区不会释放,反复切换 11 个层级会把显存吃光。useThree Composable 把这个清理收敛成一个 clearScene(),同时处理了材质为数组的情况。

动画循环注册(闭包回调)

每个场景工厂返回一个 () => void 的更新函数(自转、公转、脉冲环缩放都在里面做),主循环每帧调用当前注册的那个——setUpdate(fn) 一行切换,比维护一个 if-else 分发表干净。

技术栈

  • Vue 3: 组件框架(Composition API)
  • Three.js: 3D 渲染引擎
  • TypeScript: 类型安全
  • OrbitControls: 轨道控制器

threejs宇宙层级之旅:从地球到可观测宇宙
https://cszy.top/20260803-threejs宇宙层级之旅/
作者
csorz
发布于
2026年8月3日
许可协议