9.1 坐标辅助器与轨道控制器
9.1.1 坐标辅助器
1、three.js坐标系
当我们在three.js中创建3D场景时,我们需要使用一个坐标系来定位和控制对象的位置和方向。three.js使用的坐标系是右手坐标系,这意味着X轴向右,Y轴向上,Z轴向前。
在右手坐标系中,我们可以使用右手规则来确定一个向量的方向。右手规则指的是,将右手伸出,并将大拇指指向X轴的正方形,食指指向Y轴的正方向,中指指向Z轴的正方向。那么,当我们沿着一个向量的方向时,大拇指指向的方向就是向量的正方向。
2、添加坐标辅助器
在three.js中,它提供了一个AxesHelper类,可以用它来生成一个坐标辅助器。
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 5;
camera.lookAt(0, 0, 0);
// 添加世界坐标辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
实例化AxesHelper类的参数代表的是轴的线段长度,红色代表X轴,绿色代表Y轴,蓝色代表Z轴。
这里我们只看到X轴和Y轴,那是因为目前摄像机的位置是正对着物体,也就是说相机的位置与Z轴重叠在一起了,只需要调整一下X和Y轴即可。
// 创建一个相机 视点
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
// 设置相机的位置
camera.position.x = 2;
camera.position.y = 2;
camera.position.z = 5;
camera.lookAt(0, 0, 0);
// 添加世界坐标辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
9.1.2 轨道控制器
轨道控制器是three.js中非常实用的一个工具,它可以帮助用户更轻松地探索3D场景。使用轨道控制器,用户可以通过鼠标或触摸屏来旋转、缩放和平移相机,从而改变相机的视角,观察场景中的不同物体或角度。
具体来说,轨道控制器可以通过鼠标或触摸屏来旋转相机,改变场景的视角,调整场景的大小,移动场景中的物体。
1、引入插件库
import {OrbitControls} from "three/examples/jsm/controls/OrbitControls.js"
2、实例化轨道控制器
实例化之后并不是添加到场景就完事,而是需要调用它的update()方法然后重新渲染才能生效,一般会在它的动画函数里调用update()方法。
// 添加轨道控制器
addOrbitControls() {
this.orbitControls = new OrbitControls(this.camera, this.renderer.domElement);
},
// 动画函数
animation() {
this.orbitControls.update();
// 渲染
this.renderer.render(this.scene, this.camera);
requestAnimationFrame(this.animation);
}
注意:这里旋转缩放的并不是物体,而是相机。它是监听鼠标的拖拽和滚轮来控制相机的位置,是相机绕着物体在旋转或缩放。
3、设置带阻尼惯性
设置阻尼的意思是它滑动的时候可以带有一定的惯性,就是滑动完之后不是立即停下来,而是滑动一下再慢慢停下,比较人性化。
// 添加轨道控制器
addOrbitControls() {
this.orbitControls = new OrbitControls(this.camera, this.renderer.domElement);
// 设置带阻尼的惯性
this.orbitControls.enableDamping = true;
},
4、设置阻尼系数
我们可以通过设置阻尼系数,控制它最后滑动的时长,数值越小,惯性越大。
// 添加轨道控制器
addOrbitControls() {
this.orbitControls = new OrbitControls(this.camera, this.renderer.domElement);
// 设置带阻尼的惯性
this.orbitControls.enableDamping = true;
// 设置阻尼系数
this.orbitControls.dampingFactor = 0.01;
}
5、设置自动旋转
它提供了一个autoRotate属性,设置为true就会自动旋转,无需手动滑动。
// 添加轨道控制器
addOrbitControls() {
this.orbitControls = new OrbitControls(this.camera, this.renderer.domElement);
// 设置带阻尼的惯性
this.orbitControls.enableDamping = true;
// 设置阻尼系数
this.orbitControls.dampingFactor = 0.01;
// 设置自动旋转
this.orbitControls.autoRotate = true;
}
6、改变绑定的DOM事件
它在实例化的时候传入的第二个参数是domElement,也就是说绑定的dom元素,我们这里传入的是renderer.domElement,这个是canvas元素事件。当然如果想换一个dom元素来监听也是可以的,例如这里换成body元素。
// 添加轨道控制器
addOrbitControls() {
this.orbitControls = new OrbitControls(this.camera, document.body);
// 设置带阻尼的惯性
this.orbitControls.enableDamping = true;
// 设置阻尼系数
this.orbitControls.dampingFactor = 0.01;
// 设置自动旋转
this.orbitControls.autoRotate = true;
}
上述只是列举了轨道控制器的一部分属性,其余的属性可以参考官方文档:threejs.org/docs/index.…
9.1.3 代码示例
9.2 物体的位移与父子元素
9.2.1 物体的位移
一个三维对象基本上都存在position属性,它是用来控制这个对象的位置。它的类型是一个三维变量,默认值为(0, 0, 0)
1、直接修改position属性
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.x = 2;
2、调用set方法
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.set(2,0,0);
9.2.2 父子元素
position属性在文档上描述的是:表示对象局部位置。它并不是说绝对位置,如果它是添加在场景中的,那这个位置就是世界位置;如果它是添加在某个父元素里的,那它就是相对于父元素的局部位置。有点类似于CSS中的绝对定位和相对定位的概念。
three.js的元素不仅可以添加在场景中,还可以添加到某个物体中,形成父子关系。
1、添加一个父元素
addCube() {
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const parentCubeMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
let parentCube = new THREE.Mesh(cubeGeometry, parentCubeMaterial);
// 将子元素添加到父元素里
parentCube.add(cube);
// 添加父元素到场景里
this.scene.add(parentCube);
},
2、设置父和子元素的position
addCube() {
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const parentCubeMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
let parentCube = new THREE.Mesh(cubeGeometry, parentCubeMaterial);
// 将子元素添加到父元素里
parentCube.add(cube);
parentCube.position.set(-3, 0, 0);
cube.position.set(3, 0, 0);
// 添加父元素到场景里
this.scene.add(parentCube);
},
此时,绿色立方体为父元素,红色立方体为子元素。由于父元素的x轴坐标为-3,因此子元素的x轴设置成3,实际上是相对父元素而言,所以子元素的x轴真实坐标为0。
9.2.3 代码示例
9.3 物体的缩放与旋转
9.3.1 物体的缩放
在three.js每个物体都有scale缩放属性,它的类型是一个三维变量,分别对应x、y、z3个轴的缩放倍数。
1、使用scale进行缩放
cube.scale.set(2,2,2)
2、父子元素缩放关系
如果元素存在父子关系,这个时候就要稍微注意一下,因为scale属性跟position一样,也是相对缩放的关系。
(1)父元素缩放
addCube() {
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const parentCubeMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
let parentCube = new THREE.Mesh(cubeGeometry, parentCubeMaterial);
// 将子元素添加到父元素里
parentCube.add(cube);
parentCube.position.set(-3, 0, 0);
cube.position.set(3, 0, 0);
// 设置父元素放大
parentCube.scale.set(2,2,2)
// 添加父元素到场景里
this.scene.add(parentCube);
}
这里可以发现,父元素缩放,子元素也会跟着缩放。
(2)父子元素同时缩放
addCube() {
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const parentCubeMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
let parentCube = new THREE.Mesh(cubeGeometry, parentCubeMaterial);
// 将子元素添加到父元素里
parentCube.add(cube);
parentCube.position.set(-3, 0, 0);
cube.position.set(3, 0, 0);
// 设置父元素放大
parentCube.scale.set(2,2,2)
// 设置子元素放大
cube.scale.set(2,2,2)
// 添加父元素到场景里
this.scene.add(parentCube);
}
所以如果父子元素同时缩放,对于子元素而言,它是父元素缩放倍数与自身缩放倍数相乘。
9.3.2 物体的旋转
旋转使用的是rotation,它的参数是一个Euler欧拉角对象。欧拉角描述一个旋转变化,通过指定轴顺序和其各个轴向上的指定旋转角度来旋转一个物体。
对Euler欧拉角实例进行遍历将按相应的顺序生成它的分量(x,y,z,order)。意思是说它默认会按照x、y、z轴的顺序进行旋转,但可以对这个顺序进行调整,注意旋转顺序不同会影响旋转的效果。因此它还提供了一个order参数,让我们设置它的旋转顺序,例如order参数传入'YZX'(必须大写)那旋转顺序就是yzx轴了,不过我们按正常思维基本上还是xyz这个顺序为主。
1、使用rotation进行旋转
设置rotation使用的是弧度制,2PI是360°,PI是180°。
cube.scale.set(2,2,2)
2、父子元素旋转关系
如果元素存在父子关系,它跟scale缩放一样的道理,也是相对旋转的关系。
(1)父元素旋转
addCube() {
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const parentCubeMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
let parentCube = new THREE.Mesh(cubeGeometry, parentCubeMaterial);
// 将子元素添加到父元素里
parentCube.add(cube);
parentCube.position.set(-3, 0, 0);
cube.position.set(3, 0, 0);
// 设置父元素旋转
parentCube.rotation.x = Math.PI / 4;
}
这里可以发现,父元素旋转,子元素也会跟着旋转。
(2)父子元素同时旋转
addCube() {
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const parentCubeMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
let parentCube = new THREE.Mesh(cubeGeometry, parentCubeMaterial);
// 将子元素添加到父元素里
parentCube.add(cube);
parentCube.position.set(-3, 0, 0);
cube.position.set(3, 0, 0);
// 设置父元素旋转
parentCube.rotation.x = Math.PI / 4;
// 设置子元素旋转
cube.rotation.x = Math.PI / 4;
// 添加父元素到场景里
this.scene.add(parentCube);
}
所以如果父子元素同时旋转,对于子元素而言,它是父元素旋转的角度加上与自身旋转角度。这里父元素旋转了45°,子元素旋转了父元素45°+自身旋转45°=90°