BufferGeometry与BufferAttribute
写在最前
本篇理解和学习难度相较前面要大一些,希望各位根据自己的实际情况而定
学习ThreeJS的捷径
本段内容会写在0篇以外所有的,本人所编写的Threejs教程中
对,学习ThreeJS有捷径
当你有哪个函数不懂的时候,第一时间去翻一翻文档
当你有哪个效果不会做的时候,第一时间去翻一翻所有的案例,也许就能找到你想要的效果
最重要的一点,就是,绝对不要怕问问题,越怕找找别人问题,你的问题就会被拖的越久
如果你确定要走WebGL/ThreeJS的开发者路线的话,以下行为可以让你更快的学习ThreeJS
- 没事就把所有的文档翻一遍,哪怕看不懂,也要留个印象,至少要知道Threejs有什么
- 没事多看看案例效果,当你记忆的案例效果足够多时,下次再遇到相似问题时,你就有可能第一时间来找对应的案例,能更快解决你自己的问题
- 上述案例不只是官网的案例,郭隆邦技术博客,跃焱邵隼,暮志未晚等站点均有不少优质案例,记得一并收藏
http://www.yanhuangxueyuan.com/ 郭隆邦技术博客
https://www.wellyyss.cn/ 跃焱邵隼
http://www.wjceo.com/ 暮志未晚
这三个站点是我最常逛的站点,推荐各位有事没事逛一下,看看他们的案例和写法思路,绝对没坏处
先学会使用BufferGeometry
BufferGeometry常用属性说明
以下属性均被前面介绍的各种几何体所继承
属性名 | 值类型 | 默认值 | 说明 |
---|---|---|---|
attributes | Object | Object | 这个属性包含了几何体所需要的顶点,法线,uv,面等各类详细数据,需要了解的可以参考下BufferAttribute,在下方会着重讲解BufferAttribute |
boundingBox | Box3 | null | 包围盒,一个完美包裹了geometry的Box,可以通过.computeBoundingBox()计算,该属性用于保存该几何体的包围盒,包围盒在实际开发中使用广泛,后续的文章中会详细介绍,本篇仅做基本介绍 |
boundingSphere | Sphere | null | 包围球,一个完美包裹了geometry的Sphere,可以通过.computeBoundingSphere()计算,包围球与包围盒相似,都是使用比较广泛,所以这里仅做基本介绍 |
name,userData也为常用属性,但是笔者更多的是在Mesh中使用这两个属性而非绑定到geometry中
其他属性笔者在开发中均未使用过,有兴趣和需要的可以在官方文档中自行研究
BufferGeometry官方文档
注意:上述的Box3与Sphere两个对象,均不是geometry,不要将BoxGeometry和Box3搞混,也不要将Sphere与SphereGeometry搞混
BufferGeometry常用函数说明
函数名 | 函数说明 | 参数 | 参数说明 | 返回值 | 返回值说明 | 其他说明 |
---|---|---|---|---|---|---|
setAttribute(name,attribute) | 为当前几何体设置一个attribute属性 | name,attribute | name比较常见的有:position,uv,normal等,attribute规定为BufferAttribute类型对象 | this | 具体的在后续讲解BufferAttribute时会讲到 | |
applyMatrix4(matrix) | 用给定矩阵转换几何体的顶点坐标 | matrix | matrix为一个矩阵对象,通过矩阵对象可以变换该几何体 | this | 具体变换规则在后续会做讲解 | |
center() | 根据包围盒将几何体居中 | this | ||||
clone() | 克隆当前几何体,clone与原几何体数据不共通,clone修改后原几何体不发生变化 | BufferGeometry | 一个新的BufferGeometry对象 | |||
copy(BufferGeometry) | 将参数指定的BufferGeometry的值拷贝到当前BufferGeometry中 | bufferGeometry | 一个BufferGeometr对象 | this | 用于将其他几何体替换掉当前几何体 | |
computeBoundgBox() | 计算包围盒,计算完成后,可以通过geometry.boundingBox来访问该值 | undefined | 常用于创建好bufferGeometry后执行一次,比如说模型加载完成后,或手动创建BufferGeoemtry后,或使用了几何体合并后 | |||
computeBoundingSphere() | 计算包围球,计算完成后,可以通过geometry.boundingSphere来访问该值 | undefined | 同上 | |||
computeVertexNormals() | 通过面片法向量的平均值,计算每个顶点的法向量 | undefined | 建议创建自定义BufferGeometry时使用,法向量的问题请参阅各类数学几何学的相关说明 | |||
dispose() | 从内存中销毁该对象 | undefined | 该函数会在后续,优化篇详细讲解用途 | |||
getAttribute(name) | 根据名称获取对应的Attribute | name为指定的attribute下的属性 | BufferAttribute | 一个BufferAttribute对象 | 使用geometry.attribute.name也可以获取到对应的数据 | |
lookAt(vector) | 让几何体朝向某一点 | vector | vector为一个世界坐标的顶点 | this | 笔者更多的是使用Object3D.lookAt | |
normalizeNormals() | 法向量归一化 | this | 建议手动创建BufferGeometry后使用 | |||
rotateX(radians) | 之前介绍过,这是一种通过操作geometry数据来旋转几何体的方法 | radians | 浮点数 | this | rotateY,rotateZ下方就不做介绍了 | |
scale(x,y,z) | 与Mesh.scale.set()相似,都可以修改几何体的缩放值,但是这个缩放值是通过操作geometry数据来实现的 | x,y,z | 对应三个坐标轴向的缩放值 | this | ||
translate(x,y,z) | 与Mesh.position.set()相似,都可以修改物体的位置,但是这个是操作geometry数据来操作物体位置的 | x,y,z | 对应三个坐标轴向的偏移值 | this |
了解BufferAttribute
该部分内容有助于后续学习threejs,但是并非必须学会,看到这里的同学请根据自己的实际需要来决定是否要学习本段内容
Position与FaceIndex
要想要使用BufferGeometry去创建元素,那么就需要先了解BufferAttribute
let geometry = new THREE.BoxGeometry(1,1,1);
console.log(geometry);
我们可以这样,先查看一下官方的BoxGeometry下的Attribute都有什么
查看打印出来的内容:
这里,我们首先先看position,这里是一个长度为72的Float32类型的数组,这里threejs使用了JS中的强类型,Float32数组内的元素必定是Float类型的数据
我们将数组长度缩小三倍,72 / 3 = 24,这里记录了24个构成图形所需要的顶点数据
我们再展开下方的index
这个index,本质上是上面24个顶点,按照顺序构成三角面的face的数据
index中,有36条数据,我们也把它的长度缩小三倍,这样可以获取到12组数据,对应一个Box由12个三角面构成
我们可以看到,在index中,最大的数字为23,最小的数字为0,刚好对应了上面24个顶点的数组下标
threejs在创建三角面时,是选用了上方顶点的数据,然后在index中规定哪三个点,按照什么样的顺序,来创建对应的三角面
我们从图中来寻求验证
补全上面的代码,我们创建出一个线框的geometry
let geometry = new THREE.BoxGeometry(1,1,1);
console.log(geometry);
let material = new THREE.MeshBasicMaterial({
wireframe:true,
color:"#ff0000"
});
let mesh = new THREE.Mesh(geometry,material);
scene.add(mesh);
我们以第一个三角面开始,找到三个对应的顶点
然后进行绘制
同理我们找到第二个面与其绘制方式
这里,有些人就抱有疑问了,为什么两个都是逆时针方向绘制呢?
我们将wireframe去除,然后使用orbitControls将视角拉入到box内部,然后,我们的box消失了
let geometry = new THREE.BoxGeometry(1,1,1);
console.log(geometry);
let material = new THREE.MeshBasicMaterial({
color:"#ff0000"
});
let mesh = new THREE.Mesh(geometry,material);
scene.add(mesh);
以下为笔者对threejs研究后得到的结论:
当要创建的三角面面向你时,使用逆时针绘制,即可确定,面向你的那一面,为三角面的正面
所以我们把视角对准了box的外部时,外部均为正面,所以我们可以看到它,但是一单我们进入其内部,背面我们是看不到的
怎么样能看到背面呢?
let material = new THREE.MeshBasicMaterial({
wireframe:true,
side:THREE.BackSide
});
threejs的材质中对正反面的渲染设置了三种方式,分别是 FrontSide(正面渲染),BackSide(背面渲染),DoubleSide(双面渲染)
这些在后续的材质篇中会着重讲解,本篇仅简单介绍
Normal与UV简介
我们根据打印出来的数据发现,normal的长度与position长度相当,这里是因为normal保存的都是顶点法线,对顶点法线这部分感兴趣的可以在学习计算机图形学的过程中了解到
这里仅需要知道,没有Normal的情况下,如果geometry使用了受灯光影响的材质,这种材质将会在灯光下失效
uv后续会在Texture的文章中详细讲解,这里只需要知道,UV是对应了三角面在平面上的对应关系即可,可以直接理解为:为图形量身定做衣服时的尺子
uv上的点是在平面上分布的,所以这里的数组长度为48,对应24个顶点数据的24个2d的uv坐标
使用BufferAttribute创建自定义的三角面
这里我们直接使用了官方的写法
function addMesh(){
const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array( [
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
-1.0, -1.0, 1.0
] );
geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
console.log(geometry);
let material = new THREE.MeshBasicMaterial({
color:"#ff0000",
side:THREE.DoubleSide
});
let mesh = new THREE.Mesh(geometry,material);
scene.add(mesh);
}
我们一样,把geometry的数据打印出来
结果发现,我们的attributes仅有position一条,index是个空数组
注意:这样的Geometry是无法被贴图的,必须有一组uv坐标才能被贴上图,否则只能被渲染出纯色,同时,没有法线数据的Geometry,在使用受灯光影响的材质时,也不会对灯光起任何作用
所以我们需要生成一组点法线,这样受灯光影响的材质就有效果了
geometry.computeVertexNormals();
上述案例代码:
function addMesh(){
const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array( [
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
-1.0, -1.0, 1.0
] );
geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
geometry.computeVertexNormals();
let material = new THREE.MeshStandardMaterial({
color:"#ff0000",
side:THREE.DoubleSide
});
let mesh = new THREE.Mesh(geometry,material);
scene.add(mesh);
}
uv后续会在Texture的文章中详细讲解,这里就将无法贴图的问题保留下来
个人建议
BufferGeometry在使用上非常的麻烦,与其你想用BufferGeometry去绘制一个图形,还不如直接在建模软件中建好,然后导入到threejs中
BufferGeometry,仅推荐下列人员做深入研究:
- 涉及到【生成】某某物体时使用,如:家装项目中,用户绘制一条线,然后使用绘制的线直接生成对应的墙壁模型
- 涉及到【变形】时使用,直接修改bufferGeometry中的position,可以将图形变形,如:你想要将Box变化为一个Sphere,在老版本的threejs案例中,你还可以找到相关的案例,在新版本中该案例已经移除
- 各种底层算法,都需要你深入了解BufferGeometry
下一篇预告
BufferGeometry的实际应用