代码之家  ›  专栏  ›  技术社区  ›  Alex Wayne

你能在一次绘图调用中多次重用一个小的WebGL缓冲区吗?

  •  0
  • Alex Wayne  · 技术社区  · 4 年前

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

    例如,假设以下缓冲区

    垂直缓冲区:

    -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
    

    位置缓冲器:

    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
    

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

    attribute vec2 aVert;
    attribute vec2 aPos;
    
    uniform float uSize;
    
    void main() {
      vec2 realVertexPosition = aVert * uSize + aPos;
      gl_Position = toScreenSpaceMagic(realVertexPosition);
    }
    

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

    如果有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)
    

    然后使用如下方式渲染,假设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)
      })
    

    但我想要的是,在这个坐标系中只有六个坐标 quads 缓冲区,但调用仍然使用一个draw调用绘制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)
    

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


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

    0 回复  |  直到 4 年前