BufferGeometry: index、groups、drawRange

721 阅读3分钟

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);
example-3.PNG

如果再创建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);
example-4.PNG

总结

可以通过index减少重复顶点的出现,通过index指定顶点怎么使用,可以减少内存占用;可以通过groups对顶点进行分组绘制,每组顶点可以单独指定材质;可以通过指定drawRange进行部分渲染。