学习Three.js–太阳系星球自转公转

学习Three.js--太阳系星球自转公转

前置核心说明

开发目标

基于Three.js实现高还原度的太阳系演示场景,核心包含:

  1. 太阳(自发光+点光源)、地球(自转/公转+夜面灯光+云层)、月球(绕地球公转)的层级运动;
  2. 真实纹理贴图(星空背景、太阳/地球/月球高清纹理);
  3. 物理光照系统(太阳点光源+环境光,模拟真实光照);
  4. 后期辉光效果(Bloom)增强视觉体验;
  5. 流畅的交互控制(轨道控制器)和窗口适配。

核心技术栈

技术点 作用
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>

总结与扩展建议

核心总结

  1. 层级设计 :通过Object3D嵌套实现天体公转,是Three.js实现复杂运动的核心思路;
  2. 纹理配置 :不同纹理需对应正确的颜色空间(SRGBColorSpace/LinearSRGBColorSpace);
  3. 光照系统:「点光源(太阳)+ 环境光」组合,模拟真实物理光照;
  4. 视觉增强:后期处理(Bloom)提升氛围感,自发光/法线贴图提升真实度;
  5. 动画逻辑 :通过requestAnimationFrame更新旋转角度,实现流畅的天体运动。

扩展建议

  1. 添加更多行星:复制地球轨道逻辑,创建火星/金星等轨道容器,调整轨道半径/尺寸/纹理;
  2. 轨道线可视化 :用LineCurve绘制行星轨道线,增强可读性;
  3. 鼠标交互:添加射线检测,点击行星显示名称/信息;
  4. 性能优化:降低非关键天体的几何体分段数,或添加LOD(细节层级);
  5. 氛围增强:添加太阳耀斑、行星环(土星)、小行星带等效果。
相关推荐
恋猫de小郭6 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅13 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606114 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了14 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅14 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅14 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅15 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment15 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅15 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊15 小时前
jwt介绍
前端