【Three.js】使用Three.js开发一个太阳系 3D 可视化模拟器完整技术

【Three.js】使用Three.js开发一个太阳系 3D 可视化模拟器完整技术

项目概述

基于 Three.js 构建的交互式 3D 太阳系模拟,运行于现代浏览器中。支持行星公转、自转、土星光环、地球云层与大气、太阳辉光与日冕特效,以及相机聚焦与自由视角控制。

技术栈: Three.js r170 (ES Module)、原生 JavaScript (ES Module)、WebGL、Canvas 2D

入口文件: earth.html(单页应用,无构建工具依赖)


效果展示

项目结构

复制代码
game engine/
├── earth.html          # 主入口,包含全部 HTML/CSS/JS
├── img/                # 行星纹理图片(外网下载的 JPG)
│   ├── sun.jpg
│   ├── earth.jpg
│   ├── mercury.jpg
│   ├── venus.jpg
│   ├── mars.jpg
│   ├── jupiter.jpg
│   ├── saturn.jpg
│   ├── uranus.jpg
│   ├── neptune.jpg
│   └── moon.jpg

架构设计

加载与初始化

通过 <script type="importmap"> 映射 threethree/addons/ 到 unpkg CDN,以 ES Module 方式导入 Three.js 核心库和 OrbitControls

js 复制代码
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

场景、相机、渲染器

组件 参数
场景 黑色背景 (0x000000)
相机 PerspectiveCamera(45, aspect, 0.1, 1000),初始位置 (0, 10, 18)
渲染器 WebGLRenderer,开启抗锯齿,像素比限制 ≤2,ACES Filmic ToneMapping

轨道控制器

使用 OrbitControls 实现:

  • 阻尼惯性:dampingFactor = 0.05
  • 距离限制:minDistance = 0.8maxDistance = 60
  • 自动旋转:默认开启,速度 0.3

模块详解

1. 星空背景 (createStars)

使用 THREE.Points 生成 15000 颗星星。

  • 位置:球面均匀分布,半径 100~400
  • 大小:0.3~1.1 随机
  • 颜色:10% 偏橙红,10% 偏蓝,80% 纯白
  • 材质:PointsMaterialAdditiveBlendingsizeAttenuation: true

2. 太阳系统

纹理
  • 程序化回退纹理:橙黄渐变 + 大量随机噪点圆
  • 外载纹理:img/sun.jpg(加载完成后替换)
网格
层级 半径 材质 作用
太阳球体 1.5 MeshBasicMaterial 基础表面
内层辉光 1.6 ShaderMaterial 边缘泛光
日冕 2.0 ShaderMaterial 动态闪烁冕流
外层光晕 2.6 ShaderMaterial 微弱外发光
着色器特效

三个 ShaderMaterial 共享相同的顶点着色器(计算法线和世界坐标),区别在片元着色器:

  • 辉光 :基于视角与法线点积的边缘发光,叠加时间脉冲 sin(time * 2.0 + y * 3.0)
  • 日冕 :更强的边缘衰减 pow(rim, 3),叠加 flicker 噪声(sin(time * 1.5 + x * 5 + z * 4)
  • 光晕 :最外层大半径 pow(rim, 4),低强度 0.12

所有发光层均使用 AdditiveBlendingBackSide,保证从任何角度可见。

3. 行星系统

数据定义 (planetData)
行星 半径 轨道半径 相对速度 轴倾角 特殊标记
水星 0.08 2.5 4.15 0.03 -
金星 0.18 3.8 1.62 2.64 -
地球 0.20 5.2 1.00 0.41 earth: true
火星 0.13 6.8 0.53 0.44 -
木星 0.55 9.5 0.084 0.05 -
土星 0.45 12.5 0.034 0.47 saturn: true
天王星 0.30 15.5 0.012 1.71 -
海王星 0.28 18.5 0.006 0.49 -
纹理加载机制

采用 fallback 回退 + 异步替换 策略:

js 复制代码
const planetMatConfig = {
    mercury: { fallback: createMercuryTexture(), props: { roughness: 0.9, metalness: 0.1 } },
    // ...
};
const texUrls = {
    mercury: 'img/mercury.jpg',
    // ...
};
  1. 初始化时创建 Canvas 程序化纹理作为 fallback
  2. TextureLoader 异步加载外部 JPG 图片
  3. 加载完成后替换材质的 map 属性并标记更新

地球特殊处理:使用 MeshPhysicalMaterial 并设置 clearcoat 等 PBR 参数。

八大行星月球地球和太阳都下载号纹理图片

程序化纹理生成

各行星的 createXxxTexture() 函数均使用 Canvas 2D API:

行星 方法 风格
水星 createMercuryTexture 灰度渐变 + 大量深色陨石坑
金星 createVenusTexture 暖黄渐变 + 大范围模糊云层
地球 内联在 fallbackEarthTex 蓝绿渐变为大陆 + 白斑为云(示意)
火星 createMarsTexture 红橙渐变 + 深色地表斑点 + 浅色极冠
木星 createJupiterTexture 水平色带 + 白色湍流噪点
土星 createSaturnTexture 水平淡黄带 + 微弱高光
天王星 createUranusTexture 青蓝渐变 + 微弱云纹
海王星 createNeptuneTexture 深蓝渐变 + 淡色涡旋
LOD(多级细节)

createPlanetLOD(radius, material, baseSeg) 创建三个精度的球体:

级别 分段数 显示距离
精细 min(256, baseSeg*2) radius * 5 以内
中等 baseSeg radius * 15 以内
粗略 max(12, seg*0.4) radius * 40 以内

4. 土星环系统

纹理生成

Canvas 2048×64,绘制 23 层同心色带(startend 映射到径向位置),每层独立颜色和透明度。从内到外模拟:

  • 稠密的 B 环(明亮)
  • 卡西尼缝(低透明度间隙)
  • 较暗的 C 环
  • 外层逐渐稀薄

叠加 200 个随机噪点模拟冰粒尘埃。

UV 映射

RingGeometry 默认 UV 基于投影坐标,直接贴图会导致纹理扭曲。核心处理:

js 复制代码
for (let i = 0; i < uvs.count; i++) {
    const x = pos.getX(i);
    const y = pos.getY(i);
    const dist = Math.sqrt(x * x + y * y);
    const u = (dist - ringInner) / (ringOuter - ringInner);
    uvs.setXY(i, u, 0.5);
}
  • U 坐标重映射为径向距离归一化值,使纹理的 X 轴映射为同心环
  • V 坐标固定为 0.5(取纹理中间一行,因为所有行相同)
几何参数
参数
内径 planet.radius * 1.15
外径 planet.radius * 2.4
分段 320
倾斜角 Math.PI * 0.4
透明度 0.75
渲染面 DoubleSide

5. 地球云层与大气

  • 云层MeshPhysicalMaterial,半径偏移 0.8%,AdditiveBlending,以不同速度自转(rotation.y += dt * 0.1
  • 大气辉光ShaderMaterialpow(rim, 3) 边缘发光,颜色 #4d8affAdditiveBlending
  • 月球SphereGeometry(radius * 0.27),以地球为中心半径 0.5 公转

6. 运动系统

公转
js 复制代码
p.angle += dt * p.data.speed * 0.2;
p.lod.position.x = Math.cos(p.angle) * p.data.orbit;
p.lod.position.z = Math.sin(p.angle) * p.data.orbit;

相对速度基准:地球 = 1.0。

自转

所有行星自转:rotation.y += dt * 0.3(云层更慢 *0.1

月球公转

角速度 dt * 3,半径 0.5,绕地球旋转。

7. 相机聚焦系统

点击右侧标签触发 focusPlanet(name)

  1. 获取行星/月球世界坐标
  2. 计算目标相机位置:pos + (0, r*2, r*4)
  3. 使用 cubic bezier 缓动 插值 camera.positioncontrols.target
js 复制代码
const t = focusProgress < 0.5
    ? 4 * focusProgress * focusProgress * focusProgress
    : 1 - Math.pow(-2 * focusProgress + 2, 3) / 2;

聚焦期间暂停自动旋转。双击空白重置视角。

8. 轨道线

使用 LineBasicMaterial(透明度 0.1)绘制 128 段圆环,每条轨道在行星创建时添加。

9. UI 面板

固定于右侧垂直居中,包含所有行星和月球的圆形标签按钮。点击切换 active 高亮状态。


光照体系

光源 参数 说明
PointLight 0xffeedd, intensity 4 太阳模拟
AmbientLight 0xffffff, intensity 1 环境补光
HemisphereLight 0xffffff / 地 0x444466, 0.6 环境色

着色器一览

使用位置 着色器 核心公式
太阳辉光 ShaderMaterial (BackSide, Additive) rim = pow(1-dot(view,normal), 2) * pulse
太阳日冕 ShaderMaterial (BackSide, Additive) rim = pow(1-dot(view,normal), 3) * flicker
太阳光晕 ShaderMaterial (BackSide, Additive) rim = pow(1-dot(view,normal), 4)
地球大气 ShaderMaterial (FrontSide, Additive) rim = pow(1-dot(view,normal), 3)

关键渲染参数

参数
Pixel Ratio min(devicePixelRatio, 2)
Tone Mapping ACES Filmic
Tone Mapping Exposure 1.5
抗锯齿 antialias: true

响应式

窗口 resize 事件更新相机 aspect ratio 和渲染器尺寸。


性能优化

  1. Pixel ratio 限制Math.min(devicePixelRatio, 2) 避免高 DPI 设备渲染压力
  2. LOD 多级细节:远距离行星使用低面数网格
  3. Canvas 回退纹理:不依赖外部图片加载,首次渲染即显示
  4. 纹理尺寸控制:内行星 512×256,外行星(天王/海王)256×128
相关推荐
second602 小时前
第一部分:快速上手 —— 建立 C++ 基本语法与编程范式
开发语言·c++
郝学胜-神的一滴2 小时前
算法实战:最小k个数——大顶堆的优雅解法
开发语言·数据结构·c++·python·程序人生·算法·排序算法
bksczm2 小时前
linux之线程概念和控制
linux·开发语言·c++
柯克七七2 小时前
我把 bug 修得太干净了,测试组以为这个模块本来就没问题
前端·javascript·typescript
Null1552 小时前
浏览器唤起桌面端应用(终极篇)
前端·javascript
妙码生花2 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(三十四):后台初始化请求实现
前端·javascript·go
JackieZhengChina3 小时前
零依赖轻量级JS悬浮提示插件title2tip-js的使用教程
开发语言·javascript·ecmascript
hehelm3 小时前
Linux网络编程—TCP字典翻译系统
linux·开发语言·网络·c++·tcp/ip
大家的林语冰3 小时前
👍 Rust 为 Node 插上翅膀,反超 Bun 和 pnpm,一体化工具我也有!
前端·javascript·node.js
ssshooter4 小时前
Promise 和 async/await 被 try-catch 包裹时的行为差异
前端·javascript·面试