ThreeJs学习笔记【day8】场景【1】

238 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情 >>

前言

场景是页面的载体,场景结构是树状的,并且各自拥有各自的本地空间,类似银河系-太阳系-地球-月球这样的关系,是一个典型的树状结构

image.png 地球绕着太阳转,月球绕着地球转,月球绕着地球转了一圈。从月球的角度看,它是在地球的 "局部空间 "中旋转。尽管它相对于太阳的运动是一些疯狂的像螺线图一样的曲线,但从月球的角度来看,它只需要关注自身围绕地球这个局部空间的旋转即可

接下来,我们尝试用ThreeJs来模拟 太阳, 地球,月亮,以更好的理解场景和里面的组,元素间的关系

首先,做一个球体,放置在0,0,0点,也就是坐标系的原点位置

// 要更新旋转角度的对象数组
const objects = [];
 
// 一球多用
const radius = 1;
const widthSegments = 6;
const heightSegments = 6;
const sphereGeometry = new THREE.SphereGeometry(
  radius,
  widthSegments,
  heightSegments
);
 
const sunMaterial = new THREE.MeshPhongMaterial({ emissive: 0xffff00 });
const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
sunMesh.scale.set(5, 5, 5); // 扩大太阳的大小
scene.add(sunMesh);
objects.push(sunMesh);

image.png 还记得 SphereGeometry 吗? 我们复习一下,球体构造类,对吧, 然后radius 半径 widthSegments 经线分段 heightSegments 纬线分段 6是一个偏小的数字,因为相机角度的关系,所以看起来很像是个毛刺刺的圆,接下来就是添加光源,调整相机角度,方便我们观察

const color = 0xffffff;
const intensity = 3;
const light = new THREE.PointLight(color, intensity);
scene.add(light);

如上面所示,白色点状光源就可以了

const fov = 75;
const aspect = 2;  // 相机默认值
const near = 0.1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 10, 0);
camera.up.set(0, 0, 1);
camera.lookAt(0, 0, 0);
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();

为了便于观察,我们要把摄像头放在原点的正上方向下看。最简单的方法是使用 lookAt 函数。 lookAt 函数让摄像机从它的位置“看向”我们传递 lookAt 的位置。在这样做之前,我们需要告诉摄像机的顶部朝向哪个方向,或者说哪个方向是摄像机的 "上"。对于大多数情况来说,正 Y 是向上的就足够了,但是由于我们是直视下方,我们需要告诉摄像机正 Z 是向上的。最后的效果如下

image.png

下面就是让这个“六面体”转起来,还记得写过的requestAnimationFrame吗?在那里加上球体动起来的代码,这个时候,六面体就会动得飞快了

function render(time) {
   time *= 0.001;  // 将时间单位变为微秒要不转的太快了
 
  if (resizeRendererToDisplaySize(renderer)) {
    const canvas = renderer.domElement;
    camera.aspect = canvas.clientWidth / canvas.clientHeight;
    camera.updateProjectionMatrix();
  }
  const speed = .1  * .1;
  const rot = speed;
  objects.forEach((obj) => {
    obj.rotation.y = time;
  });
  renderer.render(scene, camera);
 
  requestAnimationFrame(render);
}
requestAnimationFrame(render);

接下来,把地球也加进去

const earthMaterial = new THREE.MeshPhongMaterial({
  color: 0x2233ff,
  emissive: 0x112244,
});
const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);
earthMesh.position.x = 10;
scene.add(earthMesh);
objects.push(earthMesh);

image.png 现在是各转各的,那有没有办法让地球绕着太阳转?我们在sunMesh上添加地球尝试一下

//scene.add(earthMesh);
sunMesh.add(earthMesh);

image.png 上图是我调整了相机位置后得到的,假如不调整,地球会看不全,而且地球变大了,因为sunMesh.scale.set(5, 5, 5);这句对sunMesh下的所有元素均生效,所有移动的距离,大小,都发生了变化,所以,我们是不是应该添加一个新的场景,然后把太阳和地球都装进去

const solarSystem = new THREE.Object3D();
scene.add(solarSystem);
objects.push(solarSystem);

把原来在scene上添加的,都加在solarSystem上,这样就可以了

地球和月亮是一个体系的,所以我们接着来一个月亮

onst earthOrbit = new THREE.Object3D();
earthOrbit.position.x = 10;
solarSystem.add(earthOrbit);
objects.push(earthOrbit);
const earthMaterial = new THREE.MeshPhongMaterial({
  color: 0x2233ff,
  emissive: 0x112244,
});
const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);
earthOrbit.add(earthMesh);
objects.push(earthMesh);
const moonOrbit = new THREE.Object3D();
moonOrbit.position.x = 2;
earthOrbit.add(moonOrbit);
 
const moonMaterial = new THREE.MeshPhongMaterial({color: 0x888888, emissive: 0x222222});
const moonMesh = new THREE.Mesh(sphereGeometry, moonMaterial);
moonMesh.scale.set(.5, .5, .5);
moonOrbit.add(moonMesh);
objects.push(moonMesh);

这里需要先把地球改造了,用Object3D来承载地和月球,到这里,一个简易的日-地-月体系就模拟完啦,下一节,将会继续探讨场景相关的内容

老惯例,示例代码通过代码片段的方式进行引入,可以调整相机的Position来观察更多细节哟