前言
到目前为止,我们的场景中只有一个立方体。这节课我们会了解到Three.js
中还有哪些预置的几何体。不过在此之前,我们先搞搞清楚,什么是几何体。
什么是几何体?
在Three.js
中,几何体由顶点
(3D空间中的点坐标) 和面
(连接这些顶点创建的三角形面) 组成。
所以我们既可以用几何体的面来创建网格模型,也可以用几何体的顶点来创建粒子。而顶点除了除了可以存储三维世界的位置信息外,还可以包含例如UV坐标或法线等更多信息。
预置的几何体
Three.js
预置了很多基本的几何体,我们并不需要去学习每一个几何体怎么用,因为学会一个就可以触类旁通举一反三,但我们仍然有必要知道究竟预置了哪些几何体。
所有预置的几何体都继承自BufferGeometry
这个类。这个类的内部包含很多通用方法,比如translate(...)
, rotateX(...)
, normalize()
等等,后面的小节中我们会逐渐地学习这些方法具体的作用。
除了这些预置的几何体,我们也可以用Javascript来自由创造几何体。还可以在其他3D软件中制作模型然后使用Three.js来加载显示它们。
BoxGeometry使用示例
虽然我们已经用BoxGeometry
创建过立方体了,但我们并没有过多的去了解创建时那些具体的参数。在使用预置几何体之前我们最好查阅一下官方文档看看到底有哪些参数。
比如BoxGeometry
在创建时可以传递6个参数:
width
: 立方体x轴的尺寸
height
: 立方体y轴的尺寸
depth
: 立方体z轴的尺寸
widthSegments
: 立方体x轴上的分段数
heightSegments
: 立方体y轴上的分段数
depthSegments
: 立方体z轴上的分段数
分段数对应了一个面上由多少三角形构成。不设置的话,默认为1,这意味着立方体的每个面由2个三角形构成。如果将三个分段参数都设置为2,则代表立方体上的1个面将由8个三角形构成。
const geometry = new THREE.BoxGeometry(1, 1, 1, 2, 2, 2)
不过暂时在立方体上还看不到我说的三角形,只需要在创建立方体材质的时候设置wireframe(线框)参数为true即可。
const material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true })
现在我们能看到这个立方体都是由三角形构成的了吧~
一个立方体的面是由2个三角形还是8个三角形或是更多三角形构成,其实看不大出来区别。但如果用在球体上,我们就可以明显的感受到球体变得更加平滑了。
const geometry = new THREE.SphereGeometry(1, 32, 32)
请注意:使用更多的顶点和面意味着更加消耗性能。
自由创造一个几何体
预置的几何体并不能满足很多真实的应用场景,我们可以使用BufferGeometry通过添加一个个的顶点信息来创建自己的几何体,但遇到复杂的模型,这样未免也太麻烦了。所以最好使用3D软件来创建,在本课程最后我们也将学习当下热门的3D建模/动画软件。但是如果需要创建的几何体并不复杂,或者可以应用某些规律来批量添加顶点信息时,那么BufferGeometry可太好用了。
首先让我们实例化一个BufferGeometry
对象:
// 创建一个空的BufferGeometry
const geometry = new THREE.BufferGeometry()
然后我们需要向BufferGeometry
对象中添加顶点坐标数据,顶点坐标数据是一个只能存储浮点数的数组。
数组中存储的顶点数据格式为x,y,z,x,y,z,x,y,z
,这代表了三个顶点分别的x轴,y轴,z轴坐标:
//可以在创建时这么写
const positionsArray = new Float32Array([
0, 0, 0, // 第一个顶点
0, 1, 0, // 第二个顶点
1, 0, 0 // 第三个顶点
])
//或者先创建一个空数组,然后依次赋值
const positionsArray = new Float32Array(9)
// 第一个顶点
positionsArray[0] = 0
positionsArray[1] = 0
positionsArray[2] = 0
// 第二个顶点
positionsArray[3] = 0
positionsArray[4] = 1
positionsArray[5] = 0
// 第三个顶点
positionsArray[6] = 1
positionsArray[7] = 0
positionsArray[8] = 0
接着我们使用这个存储了顶点坐标信息的数组来创建一个BufferAttribute
对象:
const positionsAttribute = new THREE.BufferAttribute(positionsArray, 3)
第2个参数3
代表了一个顶点信息由3个值构成。
然后我们可以使用setAttribute(...)
方法将此BufferAttribute
对象设置到我们的BufferGeometry
对象中。第一个参数是要设置的属性的名称,第二个参数是它的值:
geometry.setAttribute('position', positionsAttribute)
本例中我们将BufferAttribute
赋值给了position
属性,也就是告诉我们的多面体,这组数据用于表达坐标信息。还有其它的属性我们后边的课程里再学。
以下是全部代码:
// 创建一个空的 BufferGeometry
const geometry = new THREE.BufferGeometry()
// 创建一个 Float32Array 用于存放顶点坐标 (3 x 3)
const positionsArray = new Float32Array([
0, 0, 0, // 第一个顶点
0, 1, 0, // 第二个顶点
1, 0, 0 // 第三个顶点
])
// 创建 attribute 并赋值给 'position' 属性
const positionsAttribute = new THREE.BufferAttribute(positionsArray, 3)
geometry.setAttribute('position', positionsAttribute)
TADA!我们创建出了第一个属于我们自己的三角形。如前面所说的,我们还可以通过某种规则或没有规则的去批量创建一堆三角形:
// 创建一个空的 BufferGeometry
const geometry = new THREE.BufferGeometry()
// 创建 50 个三角形 (450 values)
const count = 50
const positionsArray = new Float32Array(count * 3 * 3)
for(let i = 0; i < count * 3 * 3; i++)
{
positionsArray[i] = (Math.random() - 0.5) * 4
}
// 创建 attribute 并赋值给 'position' 属性
const positionsAttribute = new THREE.BufferAttribute(positionsArray, 3)
geometry.setAttribute('position', positionsAttribute)
你看到count * 3 * 3
可能会疑惑,但只需要记住:
每个三角形都由三个顶点构成,每个顶点都由三个轴坐标值构成,所以是3x3
。
很显然,当我们用这种方法去构建一个立方体时,相邻的三角形的顶点会重复,本小节我们先学到这里,后面的课程里我们会再学习到如何利用BufferGeometry
的index
属性来复用完全一样的顶点信息,这将减少计算量从而提升性能。