一、THREE.Geometry弃用
参考
Three.js 新旧版本创建几何对象差异
https://sbcode.net/threejs/geometry-to-buffergeometry/
从r125开始,THREE.Geometry就被弃用了。新版的Three.js不再提供Geometry对象,代之以BufferGeometry
1.老的
function createLine(){
let start = new THREE.Vector3(7, 1.8, -2);
let end = new THREE.Vector3(1.6,1.8, -2);
// 就是这里创建的对象找不到 *****
let geometry = new THREE.Geometry();
// ****************************
let lineMaterial = new THREE.LineBasicMaterial({color: 0x00ff00});
geometry.vertices.push(start);
geometry.vertices.push(end);
let line = new THREE.Line(lineGeometry, lineMaterial);
scene.add(line);
return line;
}
2.新的
function createLine(){
let start = new THREE.Vector3(7, 1.8, -2);
let end = new THREE.Vector3(1.6,1.8, -2);
let lineMaterial = new THREE.LineBasicMaterial({color: 0x00ff00});
let pointsArray = [start, end];
let lineGeometry = new THREE.BufferGeometry().setFromPoints(pointsArray);
let line = new THREE.Line(lineGeometry, lineMaterial);
scene.add(line);
return line;
}
二、BufferGeometry设置属性
参考
https://threejs.org/docs/#api/zh/core/BufferGeometry
三维空间中绘制点、线、面、UV贴图,万能的BufferGeometry(three.js实战4)
【Three.js】BufferGeometry 基础讲解(position、normal、color、index)
three.js 中提供了一系列绘制几何体的类,如BoxGeometry、SphereGeometry,PlaneGeometry、CircleGeometry、CylinderGeometry 等,使用这些类,可以快速创建对应的几何体。参考
十二、Three.js中的常用几何体
十三、Three.js中的高级几何
three.js同时提供了对应的 BoxBufferGeometry、SphereBufferGeometry,PlaneBufferGeometry、CircleBufferGeometry、CylinderBufferGeometry等直接使用缓存的构建几何体的 xxxBufferGeometry 类簇,这些类簇相比于 xxxGeometry 类簇,可以有效减少向 GPU 传输数据所需的开销,极大提高效率,但如果提供的这些几何都不能满足需求怎么办,这时就需要用到万能的 BufferGeometry ,通过它可以向缓存中传输几何体的顶点坐标、面索引、顶点颜色、顶点法向量、顶点UV甚至是自定义属性, 使用自定义属性和着色器材质配合使用,强大到无所不能。
关于Float32Array,可以参考jsmpeg系列一 基础知识 字符处理 ArrayBuffer TypedArray
1.设置点
let positions = new Float32Array([
-10, 0, 0, // 0
10, 0, 0, // 1
0, 10, 0, // 2
0, 0, 5, // 3
0, 10, 5, // 4
0, 0, 15 // 5
]);
geometry = new THREE.BufferGeometry();
geometry.attributes.position = new THREE.BufferAttribute(positions, 3);
其中,THREE.BufferAttribute(positions, 3) 中第二个参数 3 指的是数组 positions 中每三个元素构成一个点,分别表示x y z 值。
设置好后,此时如果我们构建网格模型,代码如下。此时,会自动以positions 数组中三行数据,即9个元素3个点,连接构成一个三角形。依次构成三角形,上面的六个点将构成两个三角形,效果如图:
let mesh = new THREE.Mesh(
geometry,
new THREE.MeshBasicMaterial({
color: 0xff0000,
side: THREE.DoubleSide
})
);
scene.add(mesh);
如果使用点模型构建,则会以每3个元素创建一个点,代码、效果如下:六个点直接显示出来。
let point = new THREE.Points(
geometry,
new THREE.PointsMaterial({
color: 0x00ff00,
size: 3
})
);
scene.add(point);
如果使用线模型构建,则会按照点的顺序依次连接各个点,代码、效果如下:
let line = new THREE.Line(
geometry,
new THREE.LineBasicMaterial({
color: 0x00ffff
})
);
scene.add(line);
2.设置点颜色
每一个点都可以设置颜色,颜色通过 rgb 值来设置,r g b 分别取 0-1之间的值。比如如下设置颜色,其中,每一行分别为一个点的颜色,每一行的三个元素分别是颜色的 rgb 值,下面设置了六个点的颜色。
let colors = new Float32Array([
1, 0, 0,
0, 1, 0,
0, 0, 1,
1, 1, 0,
0, 1, 1,
1, 0, 1
]);
定义好颜色数组后,添加到 BufferGeometry 中,并且设置到材质中,举例创建一个Mesh模型如下:
geometry.addAttribute("color", new THREE.BufferAttribute(colors, 3));
let mesh = new THREE.Mesh(
geometry,
new THREE.MeshBasicMaterial({
vertexColors: THREE.VertexColors, // 该设置就是使用顶点设置颜色
side: THREE.DoubleSide
})
);
scene.add(mesh);
可以看到网格、点、线设置顶点颜色后效果分别如下:
3.设置点法向量
normal 的设置主要和光照相关,我们知道光照在物体的表面,由于光线与表面夹角角度的不一样,会导致亮度等也不一样。在 three.js 中,我们要知道一个物体的光照效果,需要知道物体表面每一个位置的法向方向,而 threejs 中的物体是由一个个点构成的,我们就需要知道每一个的法向量,这里的两个三角形法线方向分别是 z 轴和 x 轴。注意:法向量必须归一化,Vector3 中有方法可以直接调用。这里我们直接声明法向量数组:
let normals = new Float32Array([
0, 0, 1,
0, 0, 1,
0, 0, 1,
1, 0, 0,
1, 0, 0,
1, 0, 0
]);
geometry.addAttribute("normal", new THREE.BufferAttribute(normals, 3));
4.设置 index
可以理解 index 为 positions 数组中的第n-1个点,如下每一行注释后面就是该点的 index 值。我们知道在 Mesh 模型中,threejs 直接按顺序没三个点一组创建一个三角形,index 就是为了帮助我们指定哪三个点构成一个三角形,并且这些点还可以重复使用。
let positions = new Float32Array([
-10, 0, 0, // 0
10, 0, 0, // 1
0, 10, 0, // 2
0, 0, 5, // 3
0, 10, 5, // 4
0, 0, 15 // 5
]);
如下,注意:这里用的是 Uint16Array,在这里,我们就可以通过6个点创建3个三角形,并且这些三角形的顶点都是我们自己指定的。而且,这里的 THREE.BufferAttribute(indexs, 1) 的第二个参数为 1 ,表示这里数组 indexs 中每一个元素表示一个点。
let indexs = new Uint16Array([
0, 1, 2,
3, 4, 5,
2, 4, 5
]);
geometry.index = new THREE.BufferAttribute(indexs, 1);
效果如下,三个三角形:
5.绘制自定义UV贴图
关于uv的概念,可以参考图形学笔记七 纹理和贴图 AO
添加uv属性前先来捋一下uv坐标与顶点坐标的关系
上图是uv坐标与梯形顶点坐标的关系图,只需要按这个顺添加uv属性即可,需要注意的数uv坐标是一个Vector2类型,设置的时候需要注意,创建BufferAttribute时第二个参数为2,具体实现代码如下:
function drawPlaneByBufferGeometryUV() {
//创建BufferGeometry实例
const bufferGeom = new THREE.BufferGeometry();
//初始化存放顶点信息的序列化数组
const positions = new Float32Array([
-5.0, 3.0, 0.0, //point0
5.0, 3.0, 0.0, //point1
6.0, -3.0, 0.0, //point2
-6.0, -3.0, 0.0, //point3
]);
//设置顶点信息
bufferGeom.setAttribute('position', new THREE.BufferAttribute(positions, 3));
//初始化存放颜色信息的序列化数组
const colors = new Float32Array([
0.5, 0.3, 0.6,
0.5, 0.3, 0.6,
0.5, 0.3, 0.6,
0.5, 0.3, 0.6,
]);
//设置颜色信息
bufferGeom.setAttribute('color', new THREE.BufferAttribute(colors, 3));
const indexs = new Uint16Array([
0, 1, 2,
0, 2, 3,
4, 5, 6,
4, 6, 7
]);
//设置画面的索引
bufferGeom.index = new THREE.BufferAttribute(indexs, 1);
const uvs = new Uint16Array([
0, 1,
1, 1,
1, 0,
0, 0,
]);
//设置UV
bufferGeom.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));
const planetTexture = new THREE.TextureLoader().load("../assets/textures/test.png");
//创建材质
const material = new THREE.MeshBasicMaterial({
map: planetTexture,
vertexColors: THREE.VertexColors, //使用缓存中的颜色
side: THREE.DoubleSide
});
const mesh = new THREE.Mesh(bufferGeom, material);
scene.add(mesh);
}
6.addAttribute和setAttribute(新版本用这个)
THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute().
/**
* @deprecated Use {@link BufferGeometry#setAttribute .setAttribute()} instead.
*/
addAttribute(
name: string,
attribute: BufferAttribute | InterleavedBufferAttribute
): BufferGeometry;
三、THREE.BufferAttribute
参考
Object3D、Geometry和BufferAttribute的关系
【ThreeJS基础教程-高级几何体篇】2.6 BufferGeometry与BufferAttribute
深入BufferAttribute,它最重要的成员就是array: TypedArray,他就是用来给GPU发送数据的数字数组。
let positions = new Float32Array([
-10, 0, 0, // 0
10, 0, 0, // 1
0, 10, 0, // 2
0, 0, 5, // 3
0, 10, 5, // 4
0, 0, 15 // 5
]);
geometry = new THREE.BufferGeometry();
geometry.attributes.position = new THREE.BufferAttribute(positions, 3);
从直觉上来说,对数字数组中的顶点属性进行处理时,会有很多额外的计算,比如说,第二个顶点的位置,存在array[3]array[4]array[5]
,而不是array[1]
。直接处理会很麻烦,所以,three.js抽象出BufferAttribute就是解决这个痛点的。
在创建BufferAttribute时,需要一个itemSize参数,这个就是用来指定array中,几个元素表示一个顶点。对于上面的例子,每个position需要xyz三个分量,所以itemSize就是3。
BufferAttribute提供了一系列方法,按顶点来处理数据,关注点是每一个顶点,而不是数组中某一个下标的某个值。