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 );
逐个参数讲明白:
gl.TEXTURE_2D:指定操作的纹理类型,这里是普通2D纹理,WebGL还有立方体贴图等其他类型,你用的是最常见的2D纹理。0:纹理层级(mipmap level),0代表最清晰的基础层级。如果生成了多级缩放纹理(比如给远处物体用的小尺寸纹理),可以指定修改某一层级的局部区域,你的示例没用到mipmap,所以填0。0:目标纹理区域的起始X坐标(WebGL纹理坐标系左上角是(0,0)),你这里从纹理最左上角开始更新。0:目标纹理区域的起始Y坐标,从纹理最顶部开始。29:要更新的区域宽度,从X=0开始往右覆盖29个像素。29:要更新的区域高度,从Y=0开始往下覆盖29个像素。gl.RGBA:你传入的像素数据的颜色格式,这里是红、绿、蓝、透明四个通道,和你从Canvas 2D获取的imgData格式对应。gl.UNSIGNED_BYTE:每个颜色通道的数据类型,这里是0-255的无符号字节,正好匹配imgData里的通道值范围。ArrayBufferView:要上传的像素数据缓冲区,你这里是从Canvas 2D提取的图片像素,用来覆盖指定的纹理区域。
对比你之前的texImage2D调用,它需要指定整个纹理的宽高(30x30),而texSubImage2D只需要指定要修改的局部区域的位置和大小,不用改变整个纹理的尺寸。
三、权威说明获取渠道
- WebGL官方规范文档:Khronos Group发布的WebGL 1.0/2.0官方规范,里面有函数的完整定义、参数约束和行为说明,是最权威的来源。
- MDN WebGL文档:针对WebGL API的中文/英文教程,结合实际使用场景讲解,比纯规范更容易理解。
- WebGL官方教程:Khronos Group提供的官方WebGL教程,包含纹理操作的实战案例,能帮你快速掌握函数的实际用法。
内容的提问来源于stack exchange,提问作者oceanGermanique
相关产品推荐
相关产品推荐

