Three.js实现太阳系八大行星自转与公转的完整教程

作者:百宝袋时间:2026-10-23 06:17:41浏览:576

本文详细介绍如何使用Three.js框架在Web端实现太阳系八大行星的自转与公转动画。通过解析Scene、Camera和Renderer三大核心组件,结合Group层级嵌套技巧解决多行星不同速度的旋转问题,并提供完整的初始化与渲染代码示例,帮助开发者掌握WebGL 3D场景搭建与物体运动控制。

Three.js框架简介

一、Three.js框架简介

Three.js是一个基于JavaScript的WebGL第三方库。利用该框架开发3D程序,能够让用户仿佛置身于真实的3D场景中,获得沉浸式的视觉体验。在Three.js中,有三个核心基础组件:Scene(场景)、Camera(相机)和Render(渲染器)。只有正确配置这三个组件,才能将3D物体渲染到网页上,从而完成整个场景的构建。

场景(Scene)

顾名思义,Scene用于存放场景中所有的3D元素和对象。


var scene = new THREE.Scene(); //建立场景

相机(Camera)

相机决定了观察者的视角位置以及观看元素的方式。

Three.js支持多种相机类型,此处不作展开,本文示例使用的是透视相机(PerspectiveCamera)。


var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 10000); //设置相机为 角度60度,宽高比,最近端Z轴为1,最远端Z轴为10000

可以通过Three.js官方文档中的一张示意图来直观理解这些属性的含义。

Three.js框架简介

渲染器(Render)

当场景中的所有元素准备就绪后,需要调用渲染器来绘制这些元素,即确定如何将场景输出到屏幕。

渲染器也有多种类型,本示例中选用的是WebGLRenderer。


var renderer = new THREE.WebGLRenderer();

具体实现步骤包括:创建元素、定义相机、搭建场景、将元素和相机加入场景、最后执行渲染操作。

具体代码将在后文详细列出,在此之前,我们先来看一下最终的运行效果。

基本初始化设置

二、基本初始化设置

为了简化开发,这里直接通过CDN链接引入Three.js库文件。


<script src="https://cdn.bootcss.com/three.js/r83/three.min.js"></script>

注意:由于各行星的大小、转速和距离差异巨大,为了视觉效果,代码中对部分参数进行了非真实比例的平衡调整。

接下来将逐一分析各个元素是如何被添加和配置的。

1. Canvas画布设置

我们没有直接将场景挂载到body元素中,而是在body内部放置了一个canvas画布,并将3D场景显示在该画布上。

2. 背景图片处理

为了简化处理,我们没有制作3D旋转背景,而是直接放置了一张静态背景图作为“小太阳系”的背景。这张背景图是直接绘制在canvas上的。


<canvas id="webglcanvas"></canvas>
renderer = new THREE.WebGLRenderer({ //定义渲染器
   alpha: true, //让背景透明,默认是黑色,以显示我们自己的背景图
  });
renderer.setClearAlpha(0);
//css文件
#webglcanvas {
   background: url(./images/bg4.jpg) no-repeat;
   background-size: cover;
  }

然而,如果仅做上述简单操作是不够的,因为添加渲染器后,默认背景颜色为黑色。因此,需要在WebGLRenderer中设置alpha属性为true,使背景透明,从而显示出我们自定义的背景图片。

3. 定义基本组件

首先定义场景对象。


scene = new THREE.Scene(), //建立场景

接着定义相机的位置参数。


camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1,10000); //设置相机为 角度60度,宽高比,最近端Z轴为1,最远端Z轴为10000
  camera.position.z = 2000; //调整相机位置
  camera.position.y = 500;

然后创建一个Group(组)对象。

Group可以视为元素的容器,用于将具有共同特征的元素归置在一起管理。


group = new THREE.Group(), //建立一个组

在第三节中,将解释为什么需要建立额外的16个组。


 //下面这些组用来建立每个星球的父元素,以实现 八大行星不同速度的公转与自转
  var group1 = new THREE.Group();
   groupParent1 = new THREE.Group();
   group2 = new THREE.Group();
   groupParent2 = new THREE.Group();
   group3 = new THREE.Group();
   groupParent3 = new THREE.Group();
   group4 = new THREE.Group();
   groupParent4 = new THREE.Group();
   group5 = new THREE.Group();
   groupParent5 = new THREE.Group();
   group6 = new THREE.Group();
   groupParent6 = new THREE.Group();
   group7 = new THREE.Group();
   groupParent7 = new THREE.Group();
   group8 = new THREE.Group();
   groupParent8 = new THREE.Group();

定义渲染器。

WebGLRenderer中包含一个用于绘制输出的canvas对象,这里需要将之前设置的canvas对象赋值给渲染器的canvas属性。


var canvas = document.getElementById('webglcanvas'),
renderer = new THREE.WebGLRenderer({ //定义渲染器
   alpha: true, //让背景透明,默认是黑色 以显示我们自己的背景图
   canvas: canvas, //一个用来绘制输出的Canvas对象
   antialias: true //抗锯齿
  });
renderer.setSize(window.innerWidth, window.innerHeight); //设置渲染器的宽高

4. 初始化函数

在该函数中执行一系列初始化操作。


function init() {  //用来初始化的函数
   scene.add(group); //把组都添加到场景里

   scene.add(groupParent1);
   scene.add(groupParent2);
   scene.add(groupParent3);
   scene.add(groupParent4);
   scene.add(groupParent5);
   scene.add(groupParent6);
   scene.add(groupParent7);
   scene.add(groupParent8);
   
   var loader = new THREE.TextureLoader();/*材质 纹理加载器*/
   // 太阳
   loader.load('./images/sun1.jpg', function (texture) {  
    var geometry = new THREE.SphereGeometry(250, 20, 20) //球体模型 
    var material = new THREE.MeshBasicMaterial({ map: texture }) //材质 将图片解构成THREE能理解的材质
    var mesh = new THREE.Mesh(geometry, material);  //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)
    group.add(mesh);//添加到组里
   })
   // 水星
   loader.load('./images/water.jpg', function (texture) {
    var geometry = new THREE.SphereGeometry(25, 20, 20) //球型 
    var material = new THREE.MeshBasicMaterial({ map: texture }) //材质 将图片解构成THREE能理解的材质
    var mesh = new THREE.Mesh(geometry, material);
    group1.position.x -= 300;
    group1.add(mesh);
    groupParent1.add(group1);
   })
   //其它7颗行星参数因为太长了在这里就不给出了,但参数的设置原理都是一样的
   }

简要解释如下:

var loader = new THREE.TextureLoader(); 这行代码定义了一个材质纹理加载器。

var geometry = new THREE.SphereGeometry(250, 20, 20); 这行代码建立了一个球体模型,其中球体半径为250,水平分割面数量为20,垂直分割面数量也为20。

var mesh = new THREE.Mesh(geometry, material); 这行代码创建了一个网格对象(Mesh)。

其具体作用是:先创建一个球体框架,然后用行星的贴图纹理包裹该框架,从而形成一颗行星。最后将该行星添加到对应的组中,再将组添加到场景中。这就是构建单个行星元素的全过程。

那么,为什么太阳直接添加到组中,而水星等行星需要用到两个层级的组并设置位置偏移呢?这将在第三节中详细解答。

实现自转与公转

三、实现自转与公转

旋转机制:要实现旋转功能,通常有三种方式。

1. 旋转相机 2. 旋转整个场景(Scene) 3. 旋转单个元素。

由于每个行星的自转速度和公转速度各不相同,设置整体旋转并不可行,因此必须为每个元素单独设置旋转属性。

旋转机制详解:这里主要利用物体的rotation属性,实现物体相对于自身坐标系的旋转。

例如:


scene.rotation.y += 0.04; //整个场景绕自身的Y轴逆时针旋转

进入正题。

Three.js框架简介

Scene中的所有元素默认使用rotation.y属性,初始Y轴即为旋转轴。因此,让太阳旋转只需直接旋转其所在的组即可group.rotation.y += 0.04;。

而对于其他行星,若要实现绕太阳公转,需先给它们自身设置位置偏移。例如水星:group1.position.x -= 300; 此时若设置group1.rotation.y属性,由于Y轴位置已改变,它将实现自转。

Three.js框架简介

此时若想实现公转,在该对象中找不到默认的Y轴旋转线。因此,我们给group1设置了一个“父元素”groupParent1。groupParent1.add(group1);。

当我们移动group1时,groupParent1的位置保持不变,其Y轴自然也不变。由于groupParent1包含了group1,旋转groupParent1时,group1会绕着初始的默认Y轴旋转。设置这么多组的目的,正是为了实现每颗行星不同的速度,以及公转与自转的同时进行。

Three.js框架简介

其他实现函数

四、其他实现函数


  function render() {
   renderer.render(scene, camera);
   camera.lookAt(scene.position); //让相机盯着场景的位置 场景始终在中间
  }
  //设置公转
  function revolution(){
   groupParent1.rotation.y += 0.15;
   groupParent2.rotation.y += 0.065;
   groupParent3.rotation.y += 0.05;
   groupParent4.rotation.y += 0.03;
   groupParent5.rotation.y += 0.001; 
   groupParent6.rotation.y += 0.02;
   groupParent7.rotation.y += 0.0005;
   groupParent8.rotation.y += 0.003;
  }
  //设置自转
  function selfRotation(){
   group.rotation.y += 0.04;
   group1.rotation.y += 0.02;
   group2.rotation.y -= 0.005;
   group3.rotation.y += 1;
   group4.rotation.y += 1;
   group5.rotation.y += 1.5;
   group6.rotation.y += 1.5;
   group7.rotation.y -= 1.5;
   group8.rotation.y += 1.2;
  }
  function Animation() {
   render();
   selfRotation();
   revolution();
   requestAnimationFrame(Animation); 
  }

最后,再调用一下 init()和Animation()函数即可完成整个程序的运行。

总结

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对本站的支持。

作者最新文章