实例化在 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
字节。 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);
对于您告诉它每个实例使用一种颜色的颜色
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 个连续的属性位置
我想知道有什么方法可以了解在 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
字节。 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);
对于您告诉它每个实例使用一种颜色的颜色
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 个连续的属性位置