实例化在 webgl 中做了什么

what does instancing do in webgl

我想知道有什么方法可以了解在 webgl 的绘制调用中顶点着色器将被调用多少次?因为我想知道实例化到底做了什么,它是否为每个实例调用每个共享顶点?所以它会调用太多次顶点着色器

实例化应该可以为同一网格节省大量绘制调用(glDrawArrays 等)。

但是,顶点着色器仍将 运行 分别针对每个顶点和每个实例。通常应该 return 每个实例的不同值。

The OpenGL wiki explains this clearly:

The idea is that your vertex shader has some internal mechanism for deciding where each instance of the rendered mesh goes based on a single number. Perhaps it has a table (stored in a Buffer Texture or Uniform Buffer Object) that it indexes with the current vertex's instance number to get the per-instance data it needs. Perhaps it uses an attribute divisor for certain attributes, which provides a different value for each instance. Or perhaps it has a simple algorithm for computing the location of an instance based on its instance number.

实例化为每个顶点每个实例调用一个顶点着色器。不同之处在于您可以选择 1 个或多个属性每个实例只前进一次而不是每个顶点一次。

通常每个属性为每个顶点增加 stride 字节。 stridegl.vertexAttribPointer 的倒数第二个参数。如果 stride0 那么 WebGL 会根据 sizetypegl.vertexAttribPointer.

的第二个和第三个参数为你计算步幅

通过实例化,您可以为某些属性调用 gl.vertexAttribDivisor。 0 是默认的正常情况,意味着“每个顶点通过缓冲区推进一次属性”。 1 表示每个实例通过缓冲区一次推进属性。

这可能是最简单的示例。假设你有一个由 2 个三角形和 6 个顶点组成的四边形

  -1, -1, 
   1, -1,
  -1,  1,

  -1,  1,
   1, -1,
  -1, -1,

你还有 3 种颜色的缓冲区

  1, 0, 0,
  0, 1, 0,
  0, 0, 1,

你告诉 WebGL 像这样读取四边形位置

gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
const size = 2;  // 2 floats per iteration
const type = gl.FLOAT;
const normalize = false;
const stride = 0;  // let WebGL compute the stride based on size and type
const offset = 0;
gl.vertexAttribPointer(posLocation, size, type, normalize, stride, offset);

对于您告诉它每个实例使用一种颜色的颜色

gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
const size = 3;  // 3 floats per iteration
const type = gl.FLOAT;
const normalize = false;
const stride = 0;  // let WebGL compute the stride based on size and type
const offset = 0;
gl.vertexAttribPointer(colorLocation, size, type, normalize, stride, offset);
gl.vertexAttribDivisor(colorLocation, 1);

现在当你这样调用 gl.drawArraysInstanced

const mode = gl.TRIANGLES;
const first = 0;
const numVerts = 6;  // 6 verts per quad
const numInstances = 3;
gl.drawArraysInstanced(mode, first, numVerts, numInstances);

它将调用您的顶点着色器 3 * 6 次。假设你有

attribute vec2 position;
attribute vec3 color;

每次迭代的位置和颜色值将为

 iteration | position | color  | gl_InstanceID | gl_VertexID
 ----------+----------+--------+---------------+------------
     0     |  -1, -1, | 1,0,0  |      0        |    0
     1     |   1, -1, | 1,0,0  |      0        |    1
     2     |  -1,  1, | 1,0,0  |      0        |    2
     3     |  -1,  1, | 1,0,0  |      0        |    3
     4     |   1, -1, | 1,0,0  |      0        |    4
     5     |  -1, -1, | 1,0,0  |      0        |    5
     6     |  -1, -1, | 0,1,0  |      1        |    0
     7     |   1, -1, | 0,1,0  |      1        |    1
     8     |  -1,  1, | 0,1,0  |      1        |    2
     9     |  -1,  1, | 0,1,0  |      1        |    3
    10     |   1, -1, | 0,1,0  |      1        |    4
    11     |  -1, -1, | 0,1,0  |      1        |    5
    12     |  -1, -1, | 0,0,1  |      2        |    0
    13     |   1, -1, | 0,0,1  |      2        |    1
    14     |  -1,  1, | 0,0,1  |      2        |    2
    15     |  -1,  1, | 0,0,1  |      2        |    3
    16     |   1, -1, | 0,0,1  |      2        |    4
    17     |  -1, -1, | 0,0,1  |      2        |    5

请注意 gl_VertexIDgl_InstanceID 仅在 WebGL2 中可用。

上面的示例并不是很有用,因为它会直接在另一个三角形之上绘制 3 个三角形。在不同的地方绘制三角形会更有用,就像颜色属性一样,添加一个 offset 属性,每个实例有一个偏移量,并将其添加到着色器中的位置。或者更好的是,添加一个 mat4 矩阵属性并为每个实例设置一个矩阵。请注意,着色器中的 mat4 属性占用 4 个连续的属性位置