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() 只更新缓冲区的一部分数据
相关推荐
GISer_Jing1 天前
WebGL跨端兼容实战:移动端适配全攻略
前端·aigc·webgl
Aurora@Hui4 天前
WebGL & Three.js
webgl
CC码码6 天前
基于WebGPU实现canvas高级滤镜
前端·javascript·webgl·fabric
ct9786 天前
WebGL 图像处理核心API
图像处理·webgl
ct9788 天前
Cesium 矩阵系统详解
前端·线性代数·矩阵·gis·webgl
ct97811 天前
WebGL Shader性能优化
性能优化·webgl
棋鬼王11 天前
Cesium(一) 动态立体墙电子围栏,Wall墙体瀑布滚动高亮动效,基于Vue3
3d·信息可视化·智慧城市·webgl
Longyugxq13 天前
Untiy的Webgl端网页端视频播放,又不想直接mp4格式等格式的。
unity·音视频·webgl
花姐夫Jun14 天前
cesium基础学习-坐标系统相互转换及相应的场景
学习·webgl
ct97815 天前
WebGL开发
前端·gis·webgl