您可以在一次绘制调用中多次重复使用小型 WebGL 缓冲区吗?

Ale*_*yne 5 javascript webgl

现在,我正在生成大量具有相同顶点的四边形,然后将这些四边形的中心位置作为属性传递,并将比例作为统一传递。

例如,假设有以下缓冲区

顶点缓冲区:

-1,-1, -1,1, 1,1, 1,1, 1,-1, -1,1 // quad #1
-1,-1, -1,1, 1,1, 1,1, 1,-1, -1,1 // quad #2
-1,-1, -1,1, 1,1, 1,1, 1,-1, -1,1 // quad #3
-1,-1, -1,1, 1,1, 1,1, 1,-1, -1,1 // quad #3
Run Code Online (Sandbox Code Playgroud)

位置缓冲区:

10,10, 10,10, 10,10, 10,10, 10,10, 10,10 // position of quad #1
20,20, 20,20, 20,20, 20,20, 20,20, 20,20 // position of quad #2
30,30, 30,30, 30,30, 30,30, 30,30, 30,30 // position of quad #3
40,40, 40,40, 40,40, 40,40, 40,40, 40,40 // position of quad #4
Run Code Online (Sandbox Code Playgroud)

然后在顶点着色器中类似:

attribute vec2 aVert;
attribute vec2 aPos;

uniform float uSize;

void main() {
  vec2 realVertexPosition = aVert * uSize + aPos;
  gl_Position = toScreenSpaceMagic(realVertexPosition);
}
Run Code Online (Sandbox Code Playgroud)

让我感到奇怪的是这里有多少数据重复。

如果我有 1000 个四边形,那么我的相同顶点数据会重复 1000 次。每个四边形的位置重复6次。

那么有没有办法重用一个小缓冲区并告诉 WebGL 它如何重复?例如,告诉 WebGL 一个缓冲区 1000 次,其中只有一个四边形的顶点?


这是我制作顶点的方法:

// pointsToQuads returns a [number, number][] with 6 elements
// for each point, 3 points for each triangle in quad.
// Note how each quad has a `pos` (meaning the center) of [0,0].
const quads = pointsToQuads(
  layoutNodes.map(() => ({
    pos: [0, 0],
    size: { width: 1, height: 1 },
  })),
)

// calls gl.bufferData() with the flattened array of values.
this.buffers.quads.updateBuffer(quads)


// Fill a buffer of center positions for every vertex.
// dupeEntries() here changes clones each element at it's
// original index 6 times, one for each vertex of that quad.
// This duplicates the same point six times.
this.buffers.positions.updateBuffer(
  dupeEntries(
    layoutNodes.map((node) => node.position),
    6,
  ),
)

// Generate other buffers (UVs, vertex colors, etc)
Run Code Online (Sandbox Code Playgroud)

然后用类似这样的方式渲染,假设有 1000 个四边形:

  // Calls gl.useProgram()
  shaderPrograms.layoutPoints.use((shader) => {

    // Calls gl.vertexAttribPointer() for each attribute.
    shader.setAttributes({

      // here are the same six points cloned 1000 times.
      aVert: this.buffers.quads,

      aUV: this.buffers.uv,
      aIconUV: this.buffers.iconUv,
      aPos: this.buffers.positions,
      aColor: this.buffers.stateColors,
    })

    // Calls gl.uniform[1,2,3,4]f() for each uniform
    shader.setUniforms({ uSize: styles.pointsLayout.size })

    // One draw calls now renders all 1000 quads, so count here is 1000 * 6.
    gl.drawArrays(gl.TRIANGLES, 0, this.buffers.quads.count)
  })
Run Code Online (Sandbox Code Playgroud)

但我想要的是缓冲区中只有 6 个坐标quads,但调用仍然通过一次绘制调用绘制 1000 个四边形。

就像是:

// only six points in `buffer`
gl.bindBuffer(gl.ARRAY_BUFFER, buffer.glBuffer) 

// Repeat buffer 1000 times.
gl.vertexAttribPointer(attribute, 1000 * 6, gl.FLOAT, false, 0, 0)

// Then one draw call
gl.drawArrays(gl.TRIANGLES, 0, 1000 * 6)
Run Code Online (Sandbox Code Playgroud)

但这显然行不通,因为缓冲区中没有 6000 个顶点,只有 6 个。


最后,你可能会问为什么?这在一定程度上是学术性的。这种数据重复让我怀疑这是否是正确的方法。但其次,这是一个在移动设备上运行的 Expo 应用程序,其中一些移动设备的内存限制要低得多。因此,对大量重复数据进行洗牌似乎非常浪费。