如何通过WebGL将WebGLTexture绘制到Canvas/OffscreenCanvas中?
将WebGLTexture绘制到OffscreenCanvas的实现方案
核心思路
通过创建OffscreenCanvas对应的WebGL上下文,用全屏四边形配合直通着色器,将目标WebGLTexture采样后直接输出到OffscreenCanvas上。以下是具体实现步骤:
1. 准备基础环境
首先创建OffscreenCanvas并获取WebGL上下文:
// 创建指定尺寸的OffscreenCanvas const offscreenCanvas = new OffscreenCanvas(800, 600); // 获取WebGL上下文(需WebGL2可改为'webgl2') const gl = offscreenCanvas.getContext('webgl'); // 检查上下文是否创建成功 if (!gl) { console.error('WebGL上下文初始化失败'); }
2. 编写直通着色器
需要两个着色器:顶点着色器传递顶点位置和纹理坐标,片段着色器直接采样纹理输出颜色。
顶点着色器
attribute vec2 a_position; attribute vec2 a_texCoord; varying vec2 v_texCoord; void main() { // 使用标准化设备坐标,无需额外矩阵变换 gl_Position = vec4(a_position, 0.0, 1.0); // 将纹理坐标传递给片段着色器 v_texCoord = a_texCoord; }
片段着色器
precision mediump float; varying vec2 v_texCoord; uniform sampler2D u_texture; void main() { // 直接采样输入纹理,输出颜色 gl_FragColor = texture2D(u_texture, v_texCoord); }
3. 初始化WebGL资源
3.1 编译并链接着色器程序
// 编译着色器工具函数 function createShader(gl, type, source) { const shader = gl.createShader(type); gl.shaderSource(shader, source); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { console.error('着色器编译失败:', gl.getShaderInfoLog(shader)); gl.deleteShader(shader); return null; } return shader; } // 链接着色器程序工具函数 function createProgram(gl, 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)); gl.deleteProgram(program); return null; } return program; } // 编译着色器并创建程序 const vertexShader = createShader(gl, gl.VERTEX_SHADER, `顶点着色器源码`); const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, `片段着色器源码`); const program = createProgram(gl, vertexShader, fragmentShader); // 获取着色器中属性和uniform的位置 const aPositionLoc = gl.getAttribLocation(program, 'a_position'); const aTexCoordLoc = gl.getAttribLocation(program, 'a_texCoord'); const uTextureLoc = gl.getUniformLocation(program, 'u_texture');
3.2 创建全屏四边形缓冲区
用两个三角形组成覆盖整个画布的四边形,顶点数据包含位置和纹理坐标:
// 顶点数据格式:[x, y, 纹理U, 纹理V] const quadData = new Float32Array([ // 第一个三角形 -1.0, -1.0, 0.0, 0.0, 1.0, -1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, // 第二个三角形 1.0, -1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 0.0, 1.0, ]); // 创建并绑定缓冲区 const quadBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, quadBuffer); gl.bufferData(gl.ARRAY_BUFFER, quadData, gl.STATIC_DRAW);
4. 执行绘制流程
假设你已拥有目标sourceTexture(WebGLTexture实例),执行以下步骤完成绘制:
function drawTextureToOffscreen(gl, program, sourceTexture, canvasWidth, canvasHeight) { // 设置视口匹配画布尺寸 gl.viewport(0, 0, canvasWidth, canvasHeight); // 清除画布(可根据需求调整清除颜色) gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); // 使用着色器程序 gl.useProgram(program); // 启用并设置顶点属性 gl.bindBuffer(gl.ARRAY_BUFFER, quadBuffer); // 配置位置属性:每个顶点2个浮点数,步长16字节,偏移0 gl.enableVertexAttribArray(aPositionLoc); gl.vertexAttribPointer(aPositionLoc, 2, gl.FLOAT, false, 16, 0); // 配置纹理坐标属性:每个顶点2个浮点数,步长16字节,偏移8字节 gl.enableVertexAttribArray(aTexCoordLoc); gl.vertexAttribPointer(aTexCoordLoc, 2, gl.FLOAT, false, 16, 8); // 绑定输入纹理到纹理单元0 gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, sourceTexture); // 告诉着色器使用纹理单元0 gl.uniform1i(uTextureLoc, 0); // 绘制6个顶点(两个三角形组成四边形) gl.drawArrays(gl.TRIANGLES, 0, 6); } // 调用绘制函数 drawTextureToOffscreen(gl, program, sourceTexture, offscreenCanvas.width, offscreenCanvas.height);
关键注意事项
- 纹理上下文一致性:
sourceTexture必须与当前OffscreenCanvas的WebGL上下文属于同一个实例,WebGL纹理无法跨上下文直接使用。若纹理来自其他上下文,需通过texImage2D从源画布重新上传数据。 - OffscreenCanvas兼容性:部分旧版浏览器不支持OffscreenCanvas,需兼容可切换为普通Canvas,代码逻辑基本一致。
- 纹理参数设置:确保
sourceTexture的纹理参数(如gl.TEXTURE_WRAP_S、gl.TEXTURE_MIN_FILTER)已正确配置,避免采样异常。
内容的提问来源于stack exchange,提问作者savram
相关产品推荐
相关产品推荐

