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() 只更新缓冲区的一部分数据
相关推荐
qiao若huan喜2 天前
7、webgl 基本概念 + 前置数学知识点(向量 + 矩阵)
线性代数·矩阵·webgl
qiao若huan喜4 天前
6、webgl 基本概念 + 四边形纹理
前端·javascript·信息可视化·webgl
爱看书的小沐4 天前
【小沐杂货铺】基于Three.js绘制三维管道Pipe(WebGL、vue、react)
javascript·vue.js·webgl·three.js·管道·pipe·三维管道
梦凡尘6 天前
webgl 变换矩阵:旋转、平移、缩放
webgl
倚剑仙7 天前
Unity-WebGL开发——用IIS(Internet Information Services)部署webGL工程
unity·游戏引擎·webgl
xhload3d11 天前
热力图可视化为何被广泛应用?| 图扑数字孪生
3d·html5·webgl·数字孪生·可视化·热力图·三维建模·轻量化·电力能源·空间热力图
十年_H11 天前
Cesium 顶点着色器的数据来源
javascript·webgl·cesium
xhload3d17 天前
WebGL/Canvas 内存泄露分析
低代码·3d·html5·webgl·数字孪生·可视化·软件开发·工业互联网·内存泄漏·轻量化·技术应用·hightopo
爱看书的小沐19 天前
【小沐杂货铺】基于Three.js渲染三维风力发电机(WebGL、vue、react、WindTurbine)
javascript·vue.js·webgl·three.js·opengl·风力发电机·windturbine
郝学胜-神的一滴19 天前
Three.js光照技术详解:为3D场景注入灵魂
开发语言·前端·javascript·3d·web3·webgl