携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情 >>
前言
场景是页面的载体,场景结构是树状的,并且各自拥有各自的本地空间,类似银河系-太阳系-地球-月球这样的关系,是一个典型的树状结构
地球绕着太阳转,月球绕着地球转,月球绕着地球转了一圈。从月球的角度看,它是在地球的 "局部空间 "中旋转。尽管它相对于太阳的运动是一些疯狂的像螺线图一样的曲线,但从月球的角度来看,它只需要关注自身围绕地球这个局部空间的旋转即可
接下来,我们尝试用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);
还记得 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 是向上的。最后的效果如下
下面就是让这个“六面体”转起来,还记得写过的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);
现在是各转各的,那有没有办法让地球绕着太阳转?我们在sunMesh上添加地球尝试一下
//scene.add(earthMesh);
sunMesh.add(earthMesh);
上图是我调整了相机位置后得到的,假如不调整,地球会看不全,而且地球变大了,因为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来观察更多细节哟