实例化在webgl中做了什么

时间:2017-09-05 16:53:14

标签: opengl-es webgl shader instances

我想知道是否可以通过任何方式了解在webgl中的绘制调用中将调用顶点着色器的次数?因为我想知道实例化实际上做了什么,它是否为每个实例调用每个共享顶点?所以它会调用太多时间的顶点着色器

2 个答案:

答案 0 :(得分:7)

Instancing为每个实例的每个顶点调用一个顶点着色器。区别在于您可以选择一个或多个属性,每个实例仅前进一次,而不是每个顶点前进一次。

通常,每个属性为每个顶点提前stride个字节。 stridegl.vertexAttribPointer的最后一个参数的第二个。如果stride0,则WebGL会根据sizetypegl.vertexAttribPointer的第2和第3个参数为您计算一个步幅。

通过实例化,您可以针对某些属性调用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);

对于颜色,您告诉它每个实例使用1种颜色

gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
const size = 3;  // 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(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。

答案 1 :(得分:1)

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

但是,顶点着色器仍将针对每个顶点和每个实例单独运行。通常应该为每个实例返回不同的值。

The OpenGL wiki explains this clearly

  

这个想法是你的顶点着色器有一些内部机制,用于根据单个数字决定渲染网格的每个实例的位置。也许它有一个表(存储在缓冲区纹理或统一缓冲区对象中),它使用当前顶点的实例编号进行索引,以获得所需的每实例数据。也许它对某些属性使用属性除数,它为每个实例提供不同的值。或者它可能有一个简单的算法,用于根据实例编号计算实例的位置。