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 = 12000 | 12 | 99.9% |
| 位置缓冲区 | 2 * 6 * 1000 = 12000 | 2 * 1000 = 2000 | 83.3% |
| 颜色属性缓冲区 | 4 * 6 * 1000 = 24000 | 4 * 1000 = 4000 | 83.3% |
完全匹配你移动端低内存的需求,同时还能减少GPU读取缓冲区的IO开销,渲染性能也优于原有实现。
内容的提问来源于stack exchange,提问作者Alex Wayne
相关产品推荐
相关产品推荐

