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() 只更新缓冲区的一部分数据
相关推荐
心前阳光2 小时前
Unity WebGL文本输入
unity·游戏引擎·webgl
三月的一天6 小时前
React Three Fiber 实现 3D 模型点击高亮交互的核心技巧
3d·webgl·threejs·reactthreefiber
千鼎数字孪生-可视化19 小时前
Web技术栈重塑HMI开发:HTML5+WebGL的轻量化实践路径
前端·html5·webgl
Data_Adventure2 天前
推荐几款开源 Canvas 和 WebGL 图形库
前端·webgl·canvas
康康的幸福生活4 天前
webgl2 方法解析: bufferData()
webgl
xhload3d4 天前
智慧航天运载体系全生命周期监测 | 图扑数字孪生
物联网·3d·智慧城市·html5·webgl·数字孪生·可视化·工业互联网·三维建模·工控·航空航天·火箭升空·智慧航空·智慧航天·火箭发射·火箭回收
魂断蓝桥6664 天前
使用three.js,实现微信3D小游戏系列教程,框架篇(一)
webgl·threejs·微信小游戏·3d建筑·three.js路径规划、三维a*算法、javascript三维导航,·three.js小游戏
三维搬砖者5 天前
基于 Three.js 开发三维引擎-01点类:从原理到实践
webgl·three.js
魂断蓝桥6666 天前
如何基于three.js(webgl)引擎架构,实现3D微信小游戏(第一课)
webgl·three.js·微信小游戏·three.js路径规划、三维a*算法、javascript三维导航,·three.js小游戏