学习Three.js--太阳系星球自转公转
前置核心说明
开发目标
基于Three.js实现高还原度的太阳系演示场景,核心包含:
- 太阳(自发光+点光源)、地球(自转/公转+夜面灯光+云层)、月球(绕地球公转)的层级运动;
- 真实纹理贴图(星空背景、太阳/地球/月球高清纹理);
- 物理光照系统(太阳点光源+环境光,模拟真实光照);
- 后期辉光效果(Bloom)增强视觉体验;
- 流畅的交互控制(轨道控制器)和窗口适配。
核心技术栈
| 技术点 | 作用 |
|---|---|
Object3D |
构建天体层级(太阳系→地球轨道→地球/月球),实现公转运动 |
MeshStandardMaterial |
PBR物理材质,支持纹理、自发光、法线贴图等真实效果 |
PointLight |
模拟太阳光源,为行星提供光照 |
TextureLoader |
加载高清纹理贴图(星空/太阳/地球/月球) |
EffectComposer+UnrealBloomPass |
后期处理,为太阳添加辉光效果 |
OrbitControls |
鼠标交互(旋转/缩放视角) |
| 动画循环 | 实现天体自转、公转的连续运动 |
分步开发详解
步骤1:基础环境搭建(核心模块引入+三大核心创建)
1.1 引入核心模块
首先导入Three.js核心及扩展模块(轨道控制器、GLTF加载器、后期处理):
javascript
// 导入Three.js核心(CDN方式,无需本地安装)
import * as THREE from 'https://esm.sh/three@0.174.0';
// 轨道控制器(鼠标交互视角)
import { OrbitControls } from 'https://esm.sh/three@0.174.0/examples/jsm/controls/OrbitControls.js';
// 后期处理核心(辉光效果)
import { EffectComposer } from 'https://esm.sh/three@0.174.0/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'https://esm.sh/three@0.174.0/examples/jsm/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'https://esm.sh/three@0.174.0/examples/jsm/postprocessing/UnrealBloomPass.js';
1.2 创建三大核心(场景/相机/渲染器)
javascript
// 1. 场景:所有天体的容器
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000); // 初始纯黑背景(后续替换为星空)
// 2. 相机:透视相机(模拟人眼视角)
const camera = new THREE.PerspectiveCamera(
75, // 视角(FOV)
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近裁切面(最近可见距离)
2000 // 远裁切面(最远可见距离)
);
camera.position.set(0, 15, 25); // 调整相机位置,避免与天体重叠
// 3. 渲染器:将场景渲染到页面
const renderer = new THREE.WebGLRenderer({ antialias: true }); // 抗锯齿,提升画质
renderer.setSize(window.innerWidth, window.innerHeight); // 适配窗口尺寸
renderer.setPixelRatio(window.devicePixelRatio); // 适配高清屏幕
// 关键:颜色空间配置,避免纹理偏色
renderer.outputColorSpace = THREE.SRGBColorSpace;
// 色调映射:避免过曝,保留细节
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.4;
// 物理正确光照:让点光源衰减更真实
renderer.physicallyCorrectLights = true;
// 将渲染画布添加到页面
document.body.appendChild(renderer.domElement);
步骤2:天体层级设计
太阳系的核心运动逻辑是「层级嵌套」:
- 太阳系(
solarSystem):所有天体的根容器,可整体旋转; - 地球轨道(
earthOrbit):绕太阳公转的轨道容器,地球/月球挂载到该容器上,容器旋转即实现公转;
javascript
// 1. 创建太阳系根容器(所有天体的父节点)
const solarSystem = new THREE.Object3D();
// 2. 创建地球轨道容器(绕太阳公转)
const earthOrbit = new THREE.Object3D();
earthOrbit.position.x = 15; // 轨道半径(距离太阳15个单位)
// 3. 层级嵌套:地球轨道挂载到太阳系,太阳系挂载到场景
solarSystem.add(earthOrbit);
scene.add(solarSystem);
// 复用球体几何体(所有天体共用,通过scale调整大小)
const sphereGeometry = new THREE.SphereGeometry(1, 64, 64); // 半径1,分段64(平滑球体)
步骤3:纹理加载与颜色空间配置
纹理是实现真实效果的核心,需注意颜色空间配置(避免偏色):
- 普通纹理(贴图):
SRGBColorSpace; - 法线贴图:
LinearSRGBColorSpace(线性空间,计算法线更准确)。
javascript
// 创建纹理加载器
const texLoader = new THREE.TextureLoader();
// 1. 星空背景纹理
const startTexture = texLoader.load('./2k_stars_milky_way.jpg');
startTexture.colorSpace = THREE.SRGBColorSpace;
scene.background = startTexture; // 设置场景背景
// 2. 太阳纹理
const sunTexture = texLoader.load('./2k_sun.jpg');
sunTexture.colorSpace = THREE.SRGBColorSpace;
// 3. 地球纹理(日间/夜间/法线)
const earthTexture = texLoader.load('./2k_earth_daymap.jpg');
earthTexture.colorSpace = THREE.SRGBColorSpace;
const earthNightTexture = texLoader.load('./2k_earth_nightmap.jpg'); // 夜面灯光
earthNightTexture.colorSpace = THREE.SRGBColorSpace;
const earthNormalTexture = texLoader.load('./2k_earth_normalmap.jpg'); // 法线(凹凸)
earthNormalTexture.colorSpace = THREE.LinearSRGBColorSpace;
// 4. 地球云层纹理
const cloudTexture = texLoader.load('./2k_earth_clouds.jpg');
cloudTexture.colorSpace = THREE.SRGBColorSpace;
// 5. 月球纹理
const moonTexture = texLoader.load('./2k_moon.jpg');
moonTexture.colorSpace = THREE.SRGBColorSpace;
步骤4:太阳创建
太阳是场景的核心光源,需实现「自发光效果」+「点光源照亮行星」:
javascript
// 1. 太阳材质(PBR物理材质,自发光)
const sunMaterial = new THREE.MeshStandardMaterial({
map: sunTexture, // 太阳纹理
emissive: 0xffdd00, // 自发光颜色(暖黄色,贴近真实太阳)
emissiveMap: sunTexture, // 自发光跟随纹理(而非纯色)
emissiveIntensity: 0.6, // 自发光强度(避免过曝)
roughness: 0.1, // 低粗糙度,模拟太阳光滑表面
metalness: 0.0 // 非金属材质
});
// 2. 创建太阳网格模型
const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
sunMesh.scale.set(3, 3, 3); // 放大3倍(太阳尺寸远大于地球)
solarSystem.add(sunMesh); // 挂载到太阳系根容器
// 3. 太阳点光源(核心!照亮所有行星)
const sunLight = new THREE.PointLight(
0xffffff, // 光源颜色(白色)
20.0, // 强度(足够照亮地球轨道)
200, // 照射距离
1 // 衰减指数(线性衰减,更真实)
);
sunLight.position.set(0, 0, 0); // 光源与太阳位置重合
solarSystem.add(sunLight);
// 4. 环境光(补充暗部,让行星暗面可见)
const ambientLight = new THREE.AmbientLight(0x888888, 0.9);
scene.add(ambientLight);
步骤5:地球创建(基础模型+法线+夜面+云层)
地球需实现「日间纹理+夜面灯光+法线凹凸+透明云层」效果:
javascript
// 1. 地球材质(PBR物理材质)
const earthMaterial = new THREE.MeshStandardMaterial({
map: earthTexture, // 日间纹理
normalMap: earthNormalTexture, // 法线贴图(模拟地形凹凸)
normalScale: new THREE.Vector2(0.1, 0.1), // 降低法线强度(避免过度凹凸)
emissive: 0x002288, // 夜面自发光颜色(蓝色城市灯光)
emissiveMap: earthNightTexture, // 夜面纹理(暗面发光)
emissiveIntensity: 0.2, // 夜面发光强度
roughness: 0.4, // 中等粗糙度,模拟地球表面
metalness: 0.1
});
// 2. 创建地球网格模型
const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);
earthMesh.scale.set(0.8, 0.8, 0.8); // 缩小到0.8倍(地球比太阳小)
earthOrbit.add(earthMesh); // 挂载到地球轨道容器(实现公转)
// 3. 地球云层(透明效果)
const cloudMaterial = new THREE.MeshStandardMaterial({
map: cloudTexture, // 云层纹理
transparent: true, // 开启透明
opacity: 0.5 // 透明度
});
// 云层球体略大于地球(半径1.01),模拟真实云层高度
const cloudMesh = new THREE.Mesh(new THREE.SphereGeometry(1.01, 64, 64), cloudMaterial);
earthMesh.add(cloudMesh); // 挂载到地球(随地球自转)
步骤6:月球创建
月球无大气,材质更粗糙,尺寸更小:
javascript
// 1. 月球材质
const moonMaterial = new THREE.MeshStandardMaterial({
map: moonTexture, // 月球纹理
emissive: 0x111111, // 轻微自发光(灰色)
emissiveIntensity: 0.1, // 低强度(仅辅助可见)
roughness: 0.6, // 高粗糙度,模拟月球表面
metalness: 0.1
});
// 2. 创建月球网格模型
const moonMesh = new THREE.Mesh(sphereGeometry, moonMaterial);
moonMesh.scale.set(0.25, 0.25, 0.25); // 缩小到0.25倍(月球比地球小)
moonMesh.position.x = 3; // 距离地球3个单位(月球轨道半径)
earthOrbit.add(moonMesh); // 挂载到地球轨道(随地球公转,同时绕地球旋转)
步骤7:后期处理(Bloom辉光,增强太阳视觉效果)
通过UnrealBloomPass为太阳添加辉光,模拟恒星的光晕效果:
javascript
// 1. 创建渲染通道(渲染场景)
const renderScene = new RenderPass(scene, camera);
// 2. 创建辉光通道
const bloomPass = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight), // 分辨率
0.7, // 辉光强度
0.3, // 辉光半径
0.2 // 辉光阈值(仅高亮区域发光)
);
// 3. 创建后期处理合成器
const composer = new EffectComposer(renderer);
composer.addPass(renderScene); // 添加场景渲染通道
composer.addPass(bloomPass); // 添加辉光通道
步骤8:交互控制(轨道控制器)
实现鼠标/触摸交互:旋转视角、缩放距离、阻尼效果(更顺滑):
javascript
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // 开启阻尼(惯性)
controls.dampingFactor = 0.05; // 阻尼系数(越小越顺滑)
controls.minDistance = 10; // 最近可缩放距离(避免贴脸)
controls.maxDistance = 1000; // 最远可缩放距离
步骤9:动画循环(天体自转/公转)
核心是「更新天体旋转角度」+「渲染场景」,实现连续运动:
javascript
function animate() {
requestAnimationFrame(animate); // 浏览器刷新率同步
// 天体运动(速度贴合真实比例)
solarSystem.rotation.y += 0.0005; // 太阳系整体自转(慢)
earthOrbit.rotation.y += 0.008; // 地球公转(绕太阳)
sunMesh.rotation.y += 0.005; // 太阳自转
earthMesh.rotation.y += 0.02; // 地球自转(快于公转)
moonMesh.rotation.y += 0.01; // 月球自转
cloudMesh.rotation.y += 0.025; // 云层自转(略快于地球)
controls.update(); // 更新控制器状态(阻尼生效)
composer.render(); // 后期处理渲染(替代renderer.render)
}
animate(); // 启动动画循环
步骤10:窗口适配
窗口大小变化时,自动调整相机、渲染器、后期处理的尺寸:
javascript
window.addEventListener('resize', () => {
// 1. 更新相机宽高比
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
// 2. 更新渲染器尺寸
renderer.setSize(window.innerWidth, window.innerHeight);
// 3. 更新后期处理合成器尺寸
composer.setSize(window.innerWidth, window.innerHeight);
bloomPass.resolution.set(window.innerWidth, window.innerHeight);
});
关键技术点解析
1. 层级运动的核心:Object3D
场景
太阳系Object3D
太阳
地球轨道Object3D
地球
月球
云层
solarSystem.rotation.y:太阳系旋转 → 太阳/地球轨道整体旋转;earthOrbit.rotation.y:地球轨道旋转 → 地球/月球绕太阳公转;earthMesh.rotation.y:地球旋转 → 地球自转;
2. 自发光 vs 点光源
- 太阳的
emissive:视觉效果(自身发光),不影响其他物体; - 太阳的
PointLight:物理光源(照亮行星),决定行星的明暗面;
3. 后期处理的作用
UnrealBloomPass仅对「高亮区域」(如太阳自发光)添加辉光,模拟恒星的光晕效果,提升视觉层次感,且不影响行星的细节。
完整示例代码
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>Three.js 太阳系示例</title>
<style>
body { margin: 0; overflow: hidden; }
canvas { display: block; }
</style>
</head>
<body>
<script type="module">
// 1. 导入核心模块
import * as THREE from 'https://esm.sh/three@0.174.0';
import { OrbitControls } from 'https://esm.sh/three@0.174.0/examples/jsm/controls/OrbitControls.js';
import { EffectComposer } from 'https://esm.sh/three@0.174.0/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'https://esm.sh/three@0.174.0/examples/jsm/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'https://esm.sh/three@0.174.0/examples/jsm/postprocessing/UnrealBloomPass.js';
// 2. 初始化三大核心
// 场景:所有天体的容器
const scene = new THREE.Scene();
// 相机:透视相机(模拟人眼)
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 2000);
camera.position.set(0, 15, 25); // 调整视角,看清太阳系
// 渲染器:抗锯齿+高清适配
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.outputColorSpace = THREE.SRGBColorSpace; // 颜色空间(避免偏色)
renderer.toneMapping = THREE.ACESFilmicToneMapping; // 色调映射(避免过曝)
renderer.toneMappingExposure = 1.4; // 曝光值(平衡亮度)
renderer.physicallyCorrectLights = true; // 物理正确光照
document.body.appendChild(renderer.domElement);
// 3. 天体层级设计(核心:公转的基础)
const solarSystem = new THREE.Object3D(); // 太阳系根容器
const earthOrbit = new THREE.Object3D(); // 地球轨道容器
earthOrbit.position.x = 15; // 地球轨道半径(距离太阳15单位)
solarSystem.add(earthOrbit);
scene.add(solarSystem);
// 4. 纹理加载(所有纹理统一管理)
const texLoader = new THREE.TextureLoader();
// 4.1 星空背景
const starTexture = texLoader.load('./2k_stars_milky_way.jpg');
starTexture.colorSpace = THREE.SRGBColorSpace;
scene.background = starTexture;
// 4.2 太阳纹理
const sunTexture = texLoader.load('./2k_sun.jpg');
sunTexture.colorSpace = THREE.SRGBColorSpace;
// 4.3 地球纹理(日间/夜间/法线)
const earthDayTexture = texLoader.load('./2k_earth_daymap.jpg');
earthDayTexture.colorSpace = THREE.SRGBColorSpace;
const earthNightTexture = texLoader.load('./2k_earth_nightmap.jpg');
earthNightTexture.colorSpace = THREE.SRGBColorSpace;
const earthNormalTexture = texLoader.load('./2k_earth_normalmap.jpg');
earthNormalTexture.colorSpace = THREE.LinearSRGBColorSpace;
// 4.4 地球云层纹理
const cloudTexture = texLoader.load('./2k_earth_clouds.jpg');
cloudTexture.colorSpace = THREE.SRGBColorSpace;
// 4.5 月球纹理
const moonTexture = texLoader.load('./2k_moon.jpg');
moonTexture.colorSpace = THREE.SRGBColorSpace;
// 5. 创建球体几何体(复用,通过scale调整大小)
const sphereGeometry = new THREE.SphereGeometry(1, 64, 64); // 高分段,球体更平滑
// 6. 太阳创建(自发光+点光源)
const sunMaterial = new THREE.MeshStandardMaterial({
map: sunTexture,
emissive: 0xffdd00, // 暖黄色自发光
emissiveMap: sunTexture, // 自发光跟随纹理
emissiveIntensity: 0.6, // 自发光强度
roughness: 0.1,
metalness: 0.0
});
const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
sunMesh.scale.set(3, 3, 3); // 太阳放大3倍
solarSystem.add(sunMesh);
// 太阳点光源(照亮行星)
const sunLight = new THREE.PointLight(0xffffff, 20.0, 200, 1);
sunLight.position.set(0, 0, 0);
solarSystem.add(sunLight);
// 环境光(补充暗部)
const ambientLight = new THREE.AmbientLight(0x888888, 0.9);
scene.add(ambientLight);
// 7. 地球创建(日间+夜面+法线+云层)
const earthMaterial = new THREE.MeshStandardMaterial({
map: earthDayTexture,
normalMap: earthNormalTexture,
normalScale: new THREE.Vector2(0.1, 0.1), // 降低法线强度
emissive: 0x002288, // 夜面灯光颜色
emissiveMap: earthNightTexture,
emissiveIntensity: 0.2,
roughness: 0.4,
metalness: 0.1
});
const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);
earthMesh.scale.set(0.8, 0.8, 0.8); // 地球缩小0.8倍
earthOrbit.add(earthMesh);
// 地球云层
const cloudMaterial = new THREE.MeshStandardMaterial({
map: cloudTexture,
transparent: true,
opacity: 0.5
});
const cloudMesh = new THREE.Mesh(new THREE.SphereGeometry(1.01, 64, 64), cloudMaterial);
earthMesh.add(cloudMesh);
// 8. 月球创建
const moonMaterial = new THREE.MeshStandardMaterial({
map: moonTexture,
emissive: 0x111111,
emissiveIntensity: 0.1,
roughness: 0.6,
metalness: 0.1
});
const moonMesh = new THREE.Mesh(sphereGeometry, moonMaterial);
moonMesh.scale.set(0.25, 0.25, 0.25); // 月球缩小0.25倍
moonMesh.position.x = 3; // 月球轨道半径
earthOrbit.add(moonMesh);
// 9. 后期处理(辉光效果)
const renderScene = new RenderPass(scene, camera);
const bloomPass = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
0.7, 0.3, 0.2
);
const composer = new EffectComposer(renderer);
composer.addPass(renderScene);
composer.addPass(bloomPass);
// 10. 轨道控制器(交互)
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.minDistance = 10;
controls.maxDistance = 1000;
// 11. 动画循环(天体运动)
function animate() {
requestAnimationFrame(animate);
// 天体旋转(速度贴合真实比例)
solarSystem.rotation.y += 0.0005; // 太阳系自转
earthOrbit.rotation.y += 0.008; // 地球公转
sunMesh.rotation.y += 0.005; // 太阳自转
earthMesh.rotation.y += 0.02; // 地球自转
moonMesh.rotation.y += 0.01; // 月球自转
cloudMesh.rotation.y += 0.025; // 云层自转
// 更新控制器+渲染
controls.update();
composer.render();
}
animate();
// 12. 窗口适配
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
composer.setSize(window.innerWidth, window.innerHeight);
bloomPass.resolution.set(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>
总结与扩展建议
核心总结
- 层级设计 :通过
Object3D嵌套实现天体公转,是Three.js实现复杂运动的核心思路; - 纹理配置 :不同纹理需对应正确的颜色空间(
SRGBColorSpace/LinearSRGBColorSpace); - 光照系统:「点光源(太阳)+ 环境光」组合,模拟真实物理光照;
- 视觉增强:后期处理(Bloom)提升氛围感,自发光/法线贴图提升真实度;
- 动画逻辑 :通过
requestAnimationFrame更新旋转角度,实现流畅的天体运动。
扩展建议
- 添加更多行星:复制地球轨道逻辑,创建火星/金星等轨道容器,调整轨道半径/尺寸/纹理;
- 轨道线可视化 :用
LineCurve绘制行星轨道线,增强可读性; - 鼠标交互:添加射线检测,点击行星显示名称/信息;
- 性能优化:降低非关键天体的几何体分段数,或添加LOD(细节层级);
- 氛围增强:添加太阳耀斑、行星环(土星)、小行星带等效果。