You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WebGL中texSubImage2D的用途、作用及参数语义技术咨询

经过大量搜索后,我成功实现了texSubImage2D函数的运行,也写出了能呈现效果的示例代码(如下),但仍不清楚该函数的用途,完全不理解其参数的语义。我不需要语法说明,希望有人结合示例为我讲解该函数的用途、作用及参数语义,同时告知相关说明的获取渠道。

附示例代码

JavaScript

"use strict";

let canvas = document.getElementById("canvas");
let gl = canvas.getContext("webgl");

gl.canvas.width = 30;
gl.canvas.height = 30;


let vertex = `
      attribute vec2 a_position;
      attribute vec2 a_texCoord;

      uniform vec2 u_resolution;

      varying vec2 v_texCoord;

      void main() {
        vec2 zeroToOne = a_position / u_resolution;
        vec2 zeroToTwo = zeroToOne * 2.0;
        vec2 clipSpace = zeroToTwo - 1.0;
        gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
        v_texCoord = a_texCoord;
      }
    `;

let fragment = `
      precision mediump float;
      uniform sampler2D u_image;
      varying vec2 v_texCoord;
      void main() {
        gl_FragColor = texture2D(u_image, v_texCoord);
        gl_FragColor.rgb *= gl_FragColor.a;
      }
    `;

let shader = gl.createProgram();
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vertexShader, vertex);
gl.shaderSource(fragmentShader, fragment);
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
gl.attachShader(shader, vertexShader);
gl.attachShader(shader, fragmentShader);
gl.linkProgram(shader);

let image_RGBA = new Image();

image_RGBA.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeBAMAAADJHrORAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAYUExURdUAAKPTdgCN09Aq0w4A09PS0dOoXwD//56WZMcAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAA8SURBVCjPYyAIBJEBNr4SAmDnG8MALr4LBODmh4IAPn5aWhp+fjkBPgH9BOwn4H4C/icQfgTCHx9gYAAArEg8b+0tf+EAAAAASUVORK5CYII=";
image_RGBA.onload = function() {
  go(image_RGBA);
};

function go(image) {

  let width = image.width;
  let height = image.height;

  let positionLocation = gl.getAttribLocation(shader, "a_position");
  let texcoordLocation = gl.getAttribLocation(shader, "a_texCoord");

  let positionBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
    0, 0,
    width, 0,
    0, height,
    0, height,
    width, 0,
    width, height
  ]), gl.STATIC_DRAW);

  let texcoordBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
    0.0, 0.0,
    1.0, 0.0,
    0.0, 1.0,
    0.0, 1.0,
    1.0, 0.0,
    1.0, 1.0,
  ]), gl.STATIC_DRAW);

  let texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, texture);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

  const canvas2D = document.getElementById('canvas2D');
  canvas2D.width = 30;
  canvas2D.height = 30;
  const ctx = canvas2D.getContext('2d');

  ctx.drawImage(image, 0, 0);
  var imgData = ctx.getImageData(0, 0, width, height).data;
  var ArrayBufferView = new Uint8Array(imgData.buffer);

  gl.texImage2D(
    gl.TEXTURE_2D,
    0,
    gl.RGBA,
    30,
    30,
    0,
    gl.RGBA,
    gl.UNSIGNED_BYTE,
    ArrayBufferView
  );

  gl.texSubImage2D(
    gl.TEXTURE_2D,
    0,
    0,
    0,
    29,
    29,
    gl.RGBA,
    gl.UNSIGNED_BYTE,
    ArrayBufferView
  );

  let resolutionLocation = gl.getUniformLocation(shader, "u_resolution");

  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  gl.clearColor(100 / 255, 200 / 255, 150 / 255, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);

  gl.useProgram(shader);

  gl.enableVertexAttribArray(positionLocation);
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  let size = 2;
  let type = gl.FLOAT;
  let normalize = false;
  let stride = 0;
  let offset = 0;
  gl.vertexAttribPointer(positionLocation, size, type, normalize, stride, offset);

  gl.enableVertexAttribArray(texcoordLocation);
  gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
  size = 2;
  type = gl.FLOAT;
  normalize = false;
  stride = 0;
  offset = 0;
  gl.vertexAttribPointer(texcoordLocation, size, type, normalize, stride, offset);

  gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height);

  gl.enable(gl.BLEND);
  gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);

  gl.drawArrays(gl.TRIANGLES, 0, 6);
}

CSS

#canvas {
  width: 150px;
  height: 150px;
  image-rendering: pixelated;
}

#canvas2D {
  width: 150px;
  height: 150px;
  image-rendering: pixelated;
}

HTML

<canvas id="canvas2D"></canvas><canvas id="canvas"></canvas>

texSubImage2D详解(结合你的示例)

一、函数用途与核心作用

texSubImage2D是WebGL中专门用来更新已有纹理局部区域像素的工具,和texImage2D的核心区别是:

  • texImage2D会创建/替换整个纹理的所有像素数据,哪怕只需要改一小块,也得全量上传;
  • texSubImage2D只修改指定的纹理区域,不用动整个纹理,能大幅节省性能开销。

实际场景比如:

  • 像素画编辑器:用户只改了几个像素,用它更新对应小区域就行;
  • 视频流渲染:每帧只更新纹理里的视频帧区域,不用重新上传整个纹理;
  • 游戏中的动态纹理:比如角色的血量条变化,只更新血量条对应的纹理区块。

在你的示例里,你先用texImage2D把30x30的纹理填满,接着用texSubImage2D覆盖了左上角29x29的区域——这就是在演示它可以只修改纹理的一部分,而不是全量替换。

二、结合你的示例拆解参数语义

看你代码里的调用:

gl.texSubImage2D(
  gl.TEXTURE_2D,
  0,
  0,
  0,
  29,
  29,
  gl.RGBA,
  gl.UNSIGNED_BYTE,
  ArrayBufferView
);

逐个参数讲明白:

  1. gl.TEXTURE_2D:指定操作的纹理类型,这里是普通2D纹理,WebGL还有立方体贴图等其他类型,你用的是最常见的2D纹理。
  2. 0:纹理层级(mipmap level),0代表最清晰的基础层级。如果生成了多级缩放纹理(比如给远处物体用的小尺寸纹理),可以指定修改某一层级的局部区域,你的示例没用到mipmap,所以填0。
  3. 0:目标纹理区域的起始X坐标(WebGL纹理坐标系左上角是(0,0)),你这里从纹理最左上角开始更新。
  4. 0:目标纹理区域的起始Y坐标,从纹理最顶部开始。
  5. 29:要更新的区域宽度,从X=0开始往右覆盖29个像素。
  6. 29:要更新的区域高度,从Y=0开始往下覆盖29个像素。
  7. gl.RGBA:你传入的像素数据的颜色格式,这里是红、绿、蓝、透明四个通道,和你从Canvas 2D获取的imgData格式对应。
  8. gl.UNSIGNED_BYTE:每个颜色通道的数据类型,这里是0-255的无符号字节,正好匹配imgData里的通道值范围。
  9. ArrayBufferView:要上传的像素数据缓冲区,你这里是从Canvas 2D提取的图片像素,用来覆盖指定的纹理区域。

对比你之前的texImage2D调用,它需要指定整个纹理的宽高(30x30),而texSubImage2D只需要指定要修改的局部区域的位置和大小,不用改变整个纹理的尺寸。

三、权威说明获取渠道

  1. WebGL官方规范文档:Khronos Group发布的WebGL 1.0/2.0官方规范,里面有函数的完整定义、参数约束和行为说明,是最权威的来源。
  2. MDN WebGL文档:针对WebGL API的中文/英文教程,结合实际使用场景讲解,比纯规范更容易理解。
  3. WebGL官方教程:Khronos Group提供的官方WebGL教程,包含纹理操作的实战案例,能帮你快速掌握函数的实际用法。

内容的提问来源于stack exchange,提问作者oceanGermanique

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 19:10:20