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() 只更新缓冲区的一部分数据
相关推荐
刘一说14 小时前
腾讯位置服务JavaScript API GL地图组件库深度解析:Vue生态中的地理空间可视化利器
javascript·vue.js·信息可视化·webgl·webgis
烛阴1 天前
从“无”到“有”:手动实现一个 3D 渲染循环全过程
前端·webgl·three.js
WebGISer_白茶乌龙桃2 天前
Cesium实现“悬浮岛”式,三维立体的行政区划
javascript·vue.js·3d·web3·html5·webgl
烛阴2 天前
拒绝配置地狱!5 分钟搭建 Three.js + Parcel 完美开发环境
前端·webgl·three.js
WebGISer_白茶乌龙桃3 天前
Vue3 + Mapbox 加载 SHP 转换的矢量瓦片 (Vector Tiles)
javascript·vue.js·arcgis·webgl
ThreePointsHeat7 天前
Unity WebGL打包后启动方法,部署本地服务器
unity·游戏引擎·webgl
林枫依依8 天前
电脑配置流程(WebGL项目)
webgl
冥界摄政王10 天前
CesiumJS学习第四章 替换指定3D建筑模型
3d·vue·html·webgl·js·cesium
温宇飞12 天前
高效的线性采样高斯模糊
javascript·webgl
冥界摄政王13 天前
Cesium学习第一章 安装下载 基于vue3引入Cesium项目开发
vue·vue3·html5·webgl·cesium