携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第18天,点击查看活动详情 >>
前言
食用警告: 自定义缓冲几何体涉及到大量的数学和欧式空间几何的知识,需要细细思考建立空间概念才能理解,本篇文章也仅仅只是浅浅的探讨,不作深入。
在three.js中,
BufferGeometry是用来代表所有几何体的一种方式。BufferGeometry本质上是一系列BufferAttributes 的 名称 。BufferAttribute代表一种类型数据的数组:位置,法线,颜色,uv,等等…… 这些合起来,BufferAttributes 代表每个顶点所有数据的 并行数组 。
怎么去理解上面这段话?先看下面的一张图
上面提到,我们有四个属性:
position,normal,color,uv。 它们指的是 并行数组 ,代表每个属性的第N个数据集属于同一个顶点。index=4的顶点被高亮表示贯穿所有属性的平行数据定义一个顶点。
讲的白话一点是什么意思呢?就是图中vertex4所框起来的分别代表位置的数组x,y,z 代表法线的数组x,y,z,代表颜色的数组r,g,b,代表uv的数组u,v,四组平行数组共同构成了衣蛾属性集,代表的是一个顶点
我们再看一张图
考虑下方块的单个角,不同的面都需要一个不同的法线。法线是面朝向的信息。在图中,在方块的角周围用箭头表示的法线,代表共用顶点位置的面需要指向不同方向的法线。
同理,一个角在不同的面需要不同的UVs。UVs是用来指定纹理区域中,画在相应顶点位置三角形的纹理坐标。你可以看到,绿色的面需要顶点的UV对应于F纹理的右上角,蓝色的面需要的UV对应于F纹理的左上角,红色的面需要的UV对应于F纹理的左下角。
一个简单的 顶点 是所有组成部分的集合。如果顶点需要其中任一部分变得不同,那么它必须是一个不同的顶点。
举一个简单的例子,让我们创建一个使用
BufferGeometry的方块。方块很有趣,因为它看起来在角的地方共用顶点但实际上不是。在我们的例子中,我们将列出所有顶点数据,然后转化成并行数组,最后用它们创建BufferAttributes 并添加到BufferGeometry。我们从方块所需的所有数据开始。再次记住如果顶点有任何独一无二的部分,它必须是不同的顶点。像这里创建一个方块需要36个顶点,每个面2个三角形,每个三角形3个顶点,6个面=36个顶点。
const vertices = [
// front
{ pos: [-1, -1, 1], norm: [ 0, 0, 1], uv: [0, 0], },
{ pos: [ 1, -1, 1], norm: [ 0, 0, 1], uv: [1, 0], },
{ pos: [-1, 1, 1], norm: [ 0, 0, 1], uv: [0, 1], },
{ pos: [-1, 1, 1], norm: [ 0, 0, 1], uv: [0, 1], },
{ pos: [ 1, -1, 1], norm: [ 0, 0, 1], uv: [1, 0], },
{ pos: [ 1, 1, 1], norm: [ 0, 0, 1], uv: [1, 1], },
// back
{ pos: [ 1, -1, -1], norm: [ 0, 0, -1], uv: [0, 0], },
{ pos: [-1, -1, -1], norm: [ 0, 0, -1], uv: [1, 0], },
{ pos: [ 1, 1, -1], norm: [ 0, 0, -1], uv: [0, 1], },
{ pos: [ 1, 1, -1], norm: [ 0, 0, -1], uv: [0, 1], },
{ pos: [-1, -1, -1], norm: [ 0, 0, -1], uv: [1, 0], },
{ pos: [-1, 1, -1], norm: [ 0, 0, -1], uv: [1, 1], },
...
]
方块一共六个面,我们就以正面和背面为例来解释一下构成顶点的数据是如何产生的,从postion的z坐标可以看出这是一个大小为2的方块,因为正面z是1,背面z是-1 加起来为2。
然后,转化成平行数组
const positions = [];
const normals = [];
const uvs = [];
for (const vertex of vertices) {
positions.push(...vertex.pos);
normals.push(...vertex.norm);
uvs.push(...vertex.uv);
}
最后,我们把创建好的平行数组传给Buffereomtry
const geometry = new THREE.BufferGeometry();
const positionNumComponents = 3;
const normalNumComponents = 3;
const uvNumComponents = 2;
geometry.setAttribute(
'position',
new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));
geometry.setAttribute(
'normal',
new THREE.BufferAttribute(new Float32Array(normals), normalNumComponents));
geometry.setAttribute(
'uv',
new THREE.BufferAttribute(new Float32Array(uvs), uvNumComponents));
然后就能看到如下图的形状了
当然,中间的五角星是图案纹理
一般情况下,我们都是借助UI制作好的gltf等格式的3D对象来引入的,所以BufferGeomtry我们就探讨到这里吧,毕竟空间几何什么的太耗费脑细胞了,本文完整代码将会以代码片段的方式引入