You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WebGL单次draw call中能否重复使用同一个小型顶点缓冲区?

核心方案:实例化渲染(Instanced Rendering)

这项技术就是为解决大量重复几何体的绘制场景设计的,你可以仅存储1份quad的6个顶点数据,每个quad的独有属性(位置、颜色等)仅存储1次,无需重复6次,单次draw call即可完成全部绘制,内存占用可降低80%以上。


兼容说明

  • WebGL2 原生支持实例化渲染接口
  • WebGL1 环境下,绝大多数移动端设备都支持ANGLE_instanced_arrays扩展,开启后可使用等价能力

具体实现步骤

1. 缓冲区改造

  • 基础quad顶点缓冲区:仅存储1份共6个顶点的坐标,无需重复N次,永久生效
  • 实例属性缓冲区:每个quad的位置、颜色、UV偏移等独有属性,每个quad仅存1次,无需重复6次

2. 顶点属性配置

核心是使用vertexAttribDivisor接口标记属性的更新频率:

  • 基础顶点属性aVert:divisor设为0,代表每个顶点读取一次新的属性值,正常遍历6个quad顶点
  • 实例级属性aPos、aColor、aIconUV等:divisor设为1,代表每绘制完1个完整quad(6个顶点)才更新一次属性值,GPU自动复用属性,无需你手动复制

3. 绘制调用

使用实例化绘制接口替换原有的drawArrays,直接传入quad数量即可,无需乘以6


代码示例

缓冲区初始化

// 仅存一份quad顶点,大小固定为12个float
const baseQuad = new Float32Array([-1,-1, -1,1, 1,1, 1,1, 1,-1, -1,-1])
this.buffers.quads.updateBuffer(baseQuad)

// 位置缓冲区每个quad仅存1次,1000个quad仅需2000个float
const positions = layoutNodes.map(node => node.position).flat()
this.buffers.positions.updateBuffer(new Float32Array(positions))

// 其他实例属性(颜色、UV等)同理,每个quad存1次即可

渲染逻辑(WebGL2示例)

shaderPrograms.layoutPoints.use((shader) => {
  // 绑定基础顶点属性
  gl.bindBuffer(gl.ARRAY_BUFFER, this.buffers.quads.glBuffer)
  const aVertLoc = gl.getAttribLocation(shader.program, 'aVert')
  gl.vertexAttribPointer(aVertLoc, 2, gl.FLOAT, false, 0, 0)
  gl.enableVertexAttribArray(aVertLoc)
  gl.vertexAttribDivisor(aVertLoc, 0)

  // 绑定实例位置属性
  gl.bindBuffer(gl.ARRAY_BUFFER, this.buffers.positions.glBuffer)
  const aPosLoc = gl.getAttribLocation(shader.program, 'aPos')
  gl.vertexAttribPointer(aPosLoc, 2, gl.FLOAT, false, 0, 0)
  gl.enableVertexAttribArray(aPosLoc)
  gl.vertexAttribDivisor(aPosLoc, 1)

  // 其他实例属性绑定逻辑同aPos,divisor均设为1

  shader.setUniforms({ uSize: styles.pointsLayout.size })

  // 实例化绘制,直接传入quad总数,无需乘以6
  gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, layoutNodes.length)
})

WebGL1兼容代码

// 初始化阶段获取扩展
const instancedExt = gl.getExtension('ANGLE_instanced_arrays')
if (!instancedExt) {
  // 极端不支持设备可 fallback 到原有实现
}

// 渲染时替换对应接口即可
instancedExt.vertexAttribDivisorANGLE(aPosLoc, 1)
instancedExt.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 6, layoutNodes.length)

内存收益对比(以1000个quad为例)

缓冲区类型原有实现float数量实例化实现float数量压缩比例
quad顶点缓冲区12 * 1000 = 120001299.9%
位置缓冲区2 * 6 * 1000 = 120002 * 1000 = 200083.3%
颜色属性缓冲区4 * 6 * 1000 = 240004 * 1000 = 400083.3%

完全匹配你移动端低内存的需求,同时还能减少GPU读取缓冲区的IO开销,渲染性能也优于原有实现。


内容的提问来源于stack exchange,提问作者Alex Wayne

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 19:57:00