webgl2 方法解析: bufferSubData()

bufferSubData() 是 WebGL2 中用于更新缓冲区对象部分数据的方法,它不会重新分配缓冲区内存,只是替换指定范围内的数据。bufferSubData() 是 WebGL2 中高效更新缓冲区数据的重要工具,特别适合动态数据和渐进式更新场景。

基本语法

js 复制代码
void gl.bufferSubData(target, offset, srcData, srcOffset, length);

参数说明

参数 类型 描述
target GLenum 指定缓冲区类型,必须是 gl.ARRAY_BUFFERgl.ELEMENT_ARRAY_BUFFER
offset GLintptr 缓冲区中的字节偏移量,从哪里开始替换数据
srcData ArrayBufferView 数据源,可以是 ArrayBufferTypedArrayDataView
srcOffset (可选) GLuint srcData 的哪个元素开始读取
length (可选) GLuint 要复制的元素数量

使用示例

基本用法

js 复制代码
const gl = canvas.getContext('webgl2');
const buffer = gl.createBuffer();
​
// 初始化缓冲区
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(100), gl.STATIC_DRAW);
​
// 更新部分数据
const newData = new Float32Array([1, 2, 3, 4]);
gl.bufferSubData(gl.ARRAY_BUFFER, 16, newData); // 从第16字节开始更新

使用 srcOffset 和 length

js 复制代码
const fullData = new Float32Array([0,1,2,3,4,5,6,7]);
// 只更新第2-4个元素(索引1-3)
gl.bufferSubData(gl.ARRAY_BUFFER, 0, fullData, 1, 3);

完整示例

以下是一个可以直接运行的简单示例, 可以从中体会bufferSubData()的具体用法:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>bufferSubData()方法学习</title>
</head>
<body>
  <canvas id="canvas" width="600" height="400"></canvas>
  <script>
  /* ---------- 着色器 ---------- */
  const vs = `#version 300 es
  in vec2 a_pos;
  
  void main() {
    gl_Position = vec4(a_pos, 0.0, 1.0);
  }`;
​
  const fs = `#version 300 es
  precision mediump float;
  out vec4 fragColor;
  
  void main() {
    fragColor = vec4(0.2, 0.6, 1.0, 1.0);
  }`;
    
  /* ---------- 初始化 WebGL2 ---------- */
  const gl = document.getElementById('canvas').getContext('webgl2');
  if (!gl) {
    throw new Error('WebGL2 not supported! ');
  }
​
  function createShader(shaderSource, shaderType) {
​
    const shader = gl.createShader(shaderType);
    gl.shaderSource(shader, shaderSource);
    gl.compileShader(shader);
    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
      console.error(gl.getShaderInfoLog(shader));
      throw new Error('Shader compile error! ');
    }
​
    return shader;
  }
​
  function createProgram(vertexShader, fragmentShader) {
    const program = gl.createProgram();
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);
    if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
​
      console.error(gl.getProgramInfoLog(program));
      throw new Error('Program link error! ');
​
    }
​
    return program;
​
  }
​
  const vertexShader = createShader(vs, gl.VERTEX_SHADER);
  const fragmentShader = createShader(fs, gl.FRAGMENT_SHADER);
  const program = createProgram(vertexShader, fragmentShader);
​
  const posLoc = gl.getAttribLocation(program, 'a_pos');
​
  /* ---------- 缓冲区 ---------- */
  const vertexData = new Float32Array([
    // 第一个三角形
    -0.5, -0.5,
    0.0, -0.5,
    -0.5,  0.0,
    // 第二个三角形(稍后会被 bufferSubData 修改)
    0.0,  0.0,
    0.5,  0.0,
    0.0,  0.5
  ]);
  const buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  // 分配并初始化整个缓冲区
  gl.bufferData(gl.ARRAY_BUFFER, vertexData, gl.DYNAMIC_DRAW);
​
  /* ---------- 顶点数组对象 VAO ---------- */
  const vao = gl.createVertexArray();
  gl.bindVertexArray(vao);
  gl.enableVertexAttribArray(posLoc);
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
​
  /* ---------- 渲染 ---------- */
  gl.useProgram(program);
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  gl.clearColor(0, 0, 0, 1);
​
  function draw() {
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.bindVertexArray(vao);
    gl.drawArrays(gl.TRIANGLES, 0, 6);
  }
​
  draw();
​
  /* ---------- 定时用 bufferSubData 更新数据 ---------- */
  setInterval(() => {
    // 修改第二个三角形(索引 3*2=6 开始,共 3 个顶点)
    const offset = 6 * Float32Array.BYTES_PER_ELEMENT;
    const delta = 0.05; // 每次右移的量
    for (let i = 0; i < 3; ++i) {
      vertexData[6 + i*2] += delta; // 只改 x 坐标
    }
    // 把更新后的子数据刷进 GPU
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferSubData(gl.ARRAY_BUFFER, offset, vertexData.subarray(6, 12));
    draw();
  }, 1000);
​
  </script>
</body>
</html>

注意事项

  1. 偏移量对齐:偏移量必须是数据类型字节大小的倍数(如 Float32Array 必须是4的倍数)

  2. 范围检查:确保 offset + srcData.byteLength 不超过缓冲区大小,否则会抛出错误

  3. 性能考虑

    • 比重新创建整个缓冲区更高效
    • 频繁的小更新可能影响性能,考虑批量更新
  4. 同步操作bufferSubData() 是同步的,大量数据更新时可能会阻塞

与 bufferData() 的区别

方法 描述
bufferData() 重新创建并初始化整个缓冲区
bufferSubData() 只更新缓冲区的一部分数据
相关推荐
猪哥帅过吴彦祖6 小时前
第 4 篇:赋予表面生命 - WebGL 纹理贴图
前端·javascript·webgl
猪哥帅过吴彦祖1 天前
第 3 篇:让图形动起来 - WebGL 2D 变换
前端·javascript·webgl
ssshooter2 天前
WebGL 整个运行流程是怎样的?shader 是怎么从内存取到值?
前端·webgl
温宇飞6 天前
如何渲染出一个字
webgl·字体
KallkaGo6 天前
threejs复刻原神渲染(三)
前端·webgl·three.js
刘皇叔code8 天前
如何给Three.js中ExtrudeGeometry的不同面设置不同材质
webgl·three.js
Nicander11 天前
上帝视角看 GPU 学习笔记
webgl·gpu
平行云13 天前
赋能数字孪生:Paraverse平行云实时云渲染平台LarkXR,提供强大的API与SDK用于二次开发和深度集成
3d·unity·ue5·webgl·实时云渲染·云xr
刘皇叔code18 天前
记一次用Three.js展示360°全景图的折腾
webgl·three.js
xhload3d24 天前
场景切换 × 流畅过渡动画实现方案 | 图扑软件
物联网·3d·智慧城市·html5·动画·webgl·数字孪生·可视化·虚拟现实·工业互联网·工控·工业·2d·轻量化·过渡动画