BufferGeometry: index、groups、drawRange
Index
如果想画一个矩形平面,需要创建一个BufferGeoemetry,然后在它的position属性中添加6个顶点,然后通过Mesh画出来:
const rectangleGeometry = new BufferGeometry();
const rectanglePositions = [
-1, 1, 0, -1, -1, 0, 1, 1, 0, // 第一个三角形
1, 1, 0, -1, -1, 0, 1, -1, 0, // 第二个三角形
];
rectangleGeometry.setAttribute('position', new Float32BufferAttribute(rectanglePositions, 3));
const rectangle = new Mesh(rectangleGeometry, new MeshBasicMaterial({ color: 'orange' }));
可以看到,(-1, -1, 0)和(1, 1, 0)这两个点,在数组中出现了两次,这只是一个简单的矩形,对于复杂模型来说,这样重复的点会大量出现,直接写在position中没有问题,但是会造成资源的浪费。
index就是解决这个问题的。index是用来指定绘图时的顶点顺序,也就是说,position现在只存储不同的顶点,至于这些顶点怎么用来绘图,由index指定。
改一下上面的例子:
/**
* 0----2
* | |
* 1----3
*/
const rectanglePositions = [
-1, 1, 0,
-1, -1, 0,
1, 1, 0,
1,-1, 0,
];
const index = [
0, 1, 2, // 第一个三角形
2, 1, 3
];
const rectangleGeometry = new BufferGeometry();
rectangleGeometry.setAttribute('position', new Float32BufferAttribute(rectanglePositions, 3));
rectangleGeometry.setIndex(index);
const rectangle = new Mesh(rectangleGeometry, new MeshBasicMaterial({ color: 'orange' }));
groups
Split the geometry into groups, each of which will be rendered in a separate WebGL draw call. This allows an array of materials to be used with the geometry.
在使用WebGL进行绘图时,当一系列设置完成后,会调用一个draw call命令(就是一次函数调用),每次调用draw call时,会传入一组顶点(或者索引index)。当然可以一次性把所有的顶点传进去,一次drall call就可以画完。但是,有一些特殊的需求不得不分开绘制。
举个例子,现在想要再场景中展示一颗骰子,很容易就想到用BoxGeometry创建一个立方体,然后给每个面贴上不同的纹理。但是问题来了,怎么把每一个面分开绘制?
这里就要用到这个groups了,可以把立方体的6个面分别放入6个group,然后给每个group应用不同的贴图即可。如果我们使用内置的BoxGeometry,它会帮我们创建6个group。
可以看一下group的类型:
{ start: Integer, count: Integer, materialIndex: Integer }
-
start:从第几个顶点开始(如果没有指定索引index);从第几个索引开始(指定了index) -
count:从start开始数,有多少的顶点/索引。 -
materialIndex:使用的材质的下标。
还用我们上面的例子,我们给两个三角形绘制不同的颜色:
const rectangleGeometry = new BufferGeometry();
rectangleGeometry.setAttribute('position', new Float32BufferAttribute(rectanglePositions, 3));
rectangleGeometry.setIndex(index);
+ rectangleGeometry.addGroup(0, 3, 0); // 索引0, 1, 2一组,即第一个三角形
+ rectangleGeometry.addGroup(3, 3, 1); // 索引3, 4, 5一组,即第二个三角形
const materials = ['skyblue', 'orange'].map(color => new MeshBasicMaterial({ color }));
const rectangle = new Mesh(rectangleGeometry, materials);
如果再创建Mesh时,材质那个参数传入了一个材质对象,则会给所有的group应用相同的材质;如果传入一个材质数组数组,就要确保每个group中的materialIndex可以从数组中拿到材质,否则就会绘图失败。
drawRange
Determines the part of the geometry to render. This should not be set directly, instead use .setDrawRange. Default is
{ start: 0, count: Infinity }
即,部分渲染。只会渲染drawRange范围内的顶点/索引。
还拿上面的例子,现在只想画下半个三角形:
const rectangleGeometry = new BufferGeometry();
rectangleGeometry.setAttribute('position', new Float32BufferAttribute(rectanglePositions, 3));
rectangleGeometry.setIndex(index);
rectangleGeometry.addGroup(0, 3, 0); // 索引0, 1, 2一组,即第一个三角形
rectangleGeometry.addGroup(3, 3, 1); // 索引3, 4, 5一组,即第二个三角形
+ rectangleGeometry.setDrawRange(3, 3);
const materials = ['skyblue', 'orange'].map(color => new MeshBasicMaterial({ color }));
const rectangle = new Mesh(rectangleGeometry, materials);
总结
可以通过index减少重复顶点的出现,通过index指定顶点怎么使用,可以减少内存占用;可以通过groups对顶点进行分组绘制,每组顶点可以单独指定材质;可以通过指定drawRange进行部分渲染。