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

如何通过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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 22:55:28