【发布时间】:2022-01-13 10:16:40
【问题描述】:
我想知道有什么方法可以了解在 webgl 的绘图调用中将调用多少次顶点着色器?因为我想知道实例化真正做了什么,它是否为每个实例调用每个共享顶点?所以它会调用太多时间顶点着色器
【问题讨论】:
标签: opengl-es webgl shader instances
我想知道有什么方法可以了解在 webgl 的绘图调用中将调用多少次顶点着色器?因为我想知道实例化真正做了什么,它是否为每个实例调用每个共享顶点?所以它会调用太多时间顶点着色器
【问题讨论】:
标签: opengl-es webgl shader instances
Instancing 为每个实例的每个顶点调用一个顶点着色器。不同之处在于您可以选择 1 个或多个属性,每个实例只前进一次,而不是每个顶点一次。
通常每个属性为每个顶点增加stride 字节。 stride 是 gl.vertexAttribPointer 的倒数第二个参数。如果stride 是0,那么WebGL 会根据size 和type(gl.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);
对于您告诉它每个实例使用 1 种颜色的颜色
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_VertexID 和 gl_InstanceID 仅在 WebGL2 中可用。
上面的例子并没有那么有用,因为它会直接在另一个上面绘制 3 个三角形。在不同的地方绘制三角形会更有用,就像颜色属性一样,添加一个offset 属性,每个实例有一个偏移量,并将其添加到着色器中的位置。或者更好的是,添加一个mat4 矩阵属性并为每个实例设置一个矩阵。请注意,着色器中的mat4 属性占用 4 个连续的属性位置
【讨论】:
gl.vertexAttribDivisor 只是在webgl2 中吗?我读到了divisor A GLuint specifying the number of instances that will pass between updates of the generic attribute. 如果不止一个会发生什么?
gl.vertexAttribDivisor 是 WebGL1 中 ANGLE_instanced_arrays extension 的一部分。指定一个大于 1 的除数只会每 N 个实例继续处理该属性的下一个数据。例如,如果您在上表中将其设置为 2,则前 12 次迭代(前 2 个实例)将读取颜色,后 6 次迭代(最后一个实例)读取绿色
3 floats per iteration - 而不是2,对吧?
实例化应该为您节省大量相同网格的绘制调用(glDrawArrays 等)。
但是,顶点着色器仍然会为每个顶点和每个实例单独运行。它通常应该为每个实例返回不同的值。
The OpenGL wiki explains this clearly:
这个想法是,您的顶点着色器具有一些内部机制,用于根据单个数字决定渲染网格的每个实例的位置。也许它有一个表(存储在缓冲区纹理或统一缓冲区对象中),它使用当前顶点的实例编号进行索引,以获得它需要的每个实例的数据。也许它对某些属性使用属性除数,为每个实例提供不同的值。或者它有一个简单的算法,可以根据实例编号计算实例的位置。
【讨论】: