简述
现在探索Three.js功能。
在为场景制作动画之前,我们需要知道如何变换场景中的对象。
我们已经使用相机完成了此操作,方法是使用
camera.position.z = 3
有 4 个属性可用于变换对象
position(移动对象)
scale(调整对象大小)
rotation(旋转对象)
quaternion(也可以旋转对象;稍后会详细介绍)
从Object类继承的透视相机类、网格体类以及我们尚未介绍的类都具有这样的属性。
您可以在 Three.js 文档看到哪些类继承了这个类。
这些属性将被编译为我们所谓的矩阵。矩阵 在Three.js、WebGL 和 GPU 内部使用,用于变换对象坐标。幸运的是,不必自己处理矩阵,只需修改前面提到的属性即可。
设置
在一开始,画布中只有之前几节创建的立方体,立方体处在视角中心。
移动对象
position位置具有 3 个基本属性x、y、z。
每个轴的方向都是自己定义的。在 Three.js 中,我们通常认为y轴向上,z轴向后,x轴向右。
至于数量1的单位也可以是任意的,这根据自己的需要来决定。
尝试调整mesh网格体的position位置属性,推测下立方体会去哪里。
请确保在执行渲染操作render()前完成移动操作,否则网格体会在移动前被渲染。
mesh.position.x = 0.7
mesh.position.y = - 0.6
mesh.position.z = 1
position位置不是对象。它是Vector3类的一个实例,不光有x、y、z属性,还有许多拥有的方法。
可以得到向量的模
console.log(mesh.position.length())
两个Vector3类之间的距离,但以下代码先要确保创建相机后运行
console.log(mesh.position.distanceTo(camera.position))
您可以规范化其值(这意味着您将向量的长度减少到单位1,但保留其方向):
console.log(mesh.position.normalize())
若要更改值,而不是单独更改 x、y、z,还可以使用以下方法:
mesh.position.set(0.7, - 0.6, 1)
辅助轴
在空间中盲目定位很困难,尤其是在移动相机之后就更加困难。此时就需要调用Three.js的AxesHelper辅助轴。
AxesHelper辅助轴会从场景中心向x、y、z方向放射出与轴同向的线。
要创建AxesHelper辅助轴,首先需要实例化并在实例化场景后,将其添加到场景,可以通过一个参数修改线的长度。
/**
* AxesHelper辅助轴
*/
const axesHelper = new THREE.AxesHelper(2)
scene.add(axesHelper)
此时应该看到
您应该看到一条绿线和一条红线。
绿线对应于y轴。红线对应于x轴,有一条蓝线对应于z轴,但看不到它,因为它与相机完全同向。
如果需要视觉参考,请随时添加它。
缩放对象
scale缩放也是Vector3。默认情况下,x、y、z都是1。这就意味着对象没有被缩放。如果设置成0.5,对象将在该轴方向上的尺寸缩小一半;如果设置成2,对象将在该轴方向上的尺寸放大一倍。
如果更改了这些值,对象将会相应缩放。注释掉之前的position位置,然后添加缩放
mesh.scale.x = 2
mesh.scale.y = 0.25
mesh.scale.z = 0.5
很明显,无法直观的看到z轴的缩放,因为网格体是正对相机的。
缩放可以使用负值,但是会出现BUG,尽量避免这样做。
旋转对象
旋转有两个不同的属性,rotation旋转和quaternion四元数两种,当修改一个,另一个也会有相应变化。
旋转
rotation旋转也有x、y、z,但是它不是Vector3,而是欧拉数。当更改欧拉数的x、y、z时,你可以想象是绕对应轴旋转对象的操作。
旋转的值以弧度表示,如果想要实现旋转半圈,就需要写Π。在JavaScript的原生环境中,可以使用Math.PI,来表示这个数。
注释掉之前的scale缩放,然后添加绕x、y轴八分之一圈的旋转。
mesh.rotation.x = Math.PI * 0.25
mesh.rotation.y = Math.PI * 0.25
容易吧?但是当把这些旋转混合在一起,反而会出现不同的结果。其原因就在于当绕x旋转时,同时也影响了其他两个轴的方向。这就导致了旋转的结果和预期的不同。
我们可以使用reorder()方法来更正旋转的顺序,object.rotation.reorder('yxz')
虽然欧拉数易于理解,但是旋转的顺序会导致这些问题。这就是为什么大多数引擎和3D软件使用quaternion四元数方案
四元数
Quaternion四元数属性同样表达了旋转,但是使用了更数学化的方式,从而解决了顺序的问题。
这里不会讲解四元数如何运作,但是要记得Quaternion四元数随rotation旋转的变化而更新。这意味着可以随意使用两者中的任何一个。
大突破
Object3D实例有一个很棒的方法,名字是lookAt(),这个方法可以控制对象直接面向某个东西。对象会自动将-z轴旋转到提供的目标,而无需复杂的数学。
这可以被用在将相机看向物体的操作上,或是将大炮定向以面对敌人,或将角色的眼睛移动到物体上。
该参数是目标,必须是 Vector3。可以尝试创建一个:
camera.lookAt(new THREE.Vector3(0, - 1, 0))
立方体似乎变高了,但实际上,相机正在观察立方体下方。
我们也可以使用任何现有的 Vector3,例如前面的网格体的位置,但这将导致相机指向默认的位置,因为我们位于场景的中心。
camera.lookAt(mesh.position)
组合变换
可以将position(移动对象)、scale(调整对象大小)、rotation(旋转对象 也可以用quaternion)以任意顺序组合。运行的结果将是相同的。因为它就是对象的状态。
尝试把之前的所有变换组合在一起
mesh.position.x = 0.7
mesh.position.y = - 0.6
mesh.position.z = 1
mesh.scale.x = 2
mesh.scale.y = 0.25
mesh.scale.z = 0.5
mesh.rotation.x = Math.PI * 0.25
mesh.rotation.y = Math.PI * 0.25
场景分组
在某些时候,可能希望对事物进行分组。假设正在建造一座有墙壁,门,窗户,屋顶,灌木丛等的房子。
当认为已经完成了时,可能就会意识到房子太小了,那么必须重新缩放每个对象并更新它们的位置。
一个好的替代方法是将所有这些对象分组到一个容器中,并缩放该容器。
可以使用Group组类执行此操作。
实例化Group组,并把它添加到场景。现在,当要创建一个新的对象的时候,只需使用add()方法把他添加到组中,而非直接添加到场景中。
由于Group组类继承了Object3D类,所以它也具备前述的所有属性和方法。
注释掉之前的lookAt()调用,创建三个立方体并添加到一个组中,来替代之前的立方体。然后向组应用变换。
/**
* 创建对象
*/
const group = new THREE.Group()
group.scale.y = 2
group.rotation.y = 0.2
scene.add(group)
const cube1 = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshBasicMaterial({ color: 0xff0000 })
)
cube1.position.x = - 1.5
group.add(cube1)
const cube2 = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshBasicMaterial({ color: 0xff0000 })
)
cube2.position.x = 0
group.add(cube2)
const cube3 = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshBasicMaterial({ color: 0xff0000 })
)
cube3.position.x = 1.5
group.add(cube3)
顺序不重要,重要的是它是有效的JavaScript。