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

如何在HTML5 Canvas中用WebGPU更新2D图像的像素数据?

WebGL 转 WebGPU:纹理更新与渲染实现

核心步骤说明

WebGPU 的纹理处理与渲染流程和 WebGL 存在明显差异,核心区别在于:WebGPU 采用管线化设计,纹理采样参数单独通过采样器(Sampler)配置,数据上传、渲染命令需要显式编码提交。以下是对应你提供的 WebGL 代码的完整 WebGPU 实现:

前置条件

假设你已完成 WebGPU 基础初始化:

  • 获取了 GPUAdapter 和 GPUDevice
  • 获取了 Canvas 的 GPUCanvasContext

完整实现代码

// 1. 创建纹理(对应 WebGL 的 createTexture)
const texture = device.createTexture({
  size: [image.width, image.height],
  format: 'rgba8unorm', // 对应 WebGL 的 RGBA/UNSIGNED_BYTE
  usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT
});

// 2. 上传 ImageData 到纹理(对应 WebGL 的 texImage2D)
device.queue.writeTexture(
  { texture: texture }, // 目标纹理
  image.data, // ImageData 的像素数据
  {
    bytesPerRow: 4 * image.width, // 每行字节数:RGBA 4通道 × 宽度
    rowsPerImage: image.height
  },
  { width: image.width, height: image.height } // 纹理尺寸
);

// 3. 创建采样器(对应 WebGL 的 texParameteri 配置)
const sampler = device.createSampler({
  magFilter: 'nearest', // 对应 WebGL 的 TEXTURE_MAG_FILTER=NEAREST
  minFilter: 'nearest', // 对应 WebGL 的 TEXTURE_MIN_FILTER=NEAREST
  addressModeU: 'clamp-to-edge', // 对应 WebGL 的 TEXTURE_WRAP_S=CLAMP_TO_EDGE
  addressModeV: 'clamp-to-edge'  // 对应 WebGL 的 TEXTURE_WRAP_T=CLAMP_TO_EDGE
});

// 4. 定义着色器(WebGPU 必须显式编写顶点/片段着色器)
const shaderModule = device.createShaderModule({
  code: `
    struct VertexOutput {
      @location(0) uv: vec2f,
      @builtin(position) position: vec4f
    };

    @vertex
    fn vertexMain(@location(0) position: vec2f, @location(1) uv: vec2f) -> VertexOutput {
      var output: VertexOutput;
      output.position = vec4f(position, 0.0, 1.0);
      output.uv = uv;
      return output;
    }

    @group(0) @binding(0) var mySampler: sampler;
    @group(0) @binding(1) var myTexture: texture_2d<f32>;

    @fragment
    fn fragmentMain(input: VertexOutput) -> @location(0) vec4f {
      return textureSample(myTexture, mySampler, input.uv);
    }
  `
});

// 5. 创建渲染管线
const pipeline = device.createRenderPipeline({
  layout: 'auto',
  vertex: {
    module: shaderModule,
    entryPoint: 'vertexMain',
    buffers: [{
      arrayStride: 16, // 每个顶点:vec2(position) + vec2(uv) = 4×4字节
      attributes: [
        { shaderLocation: 0, offset: 0, format: 'float32x2' }, // position
        { shaderLocation: 1, offset: 8, format: 'float32x2' }  // uv
      ]
    }]
  },
  fragment: {
    module: shaderModule,
    entryPoint: 'fragmentMain',
    targets: [{ format: canvasContext.getPreferredFormat(device) }]
  },
  primitive: { topology: 'triangle-list' } // 对应 WebGL 的 TRIANGLES
});

// 6. 创建顶点缓冲区(全屏四边形的6个顶点数据)
const vertexData = new Float32Array([
  // position, uv
  -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, 0.0, 1.0,
   1.0, -1.0, 1.0, 0.0,
   1.0,  1.0, 1.0, 1.0
]);
const vertexBuffer = device.createBuffer({
  size: vertexData.byteLength,
  usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
});
device.queue.writeBuffer(vertexBuffer, 0, vertexData);

// 7. 创建绑定组(将纹理和采样器绑定到管线)
const bindGroup = device.createBindGroup({
  layout: pipeline.getBindGroupLayout(0),
  entries: [
    { binding: 0, resource: sampler },
    { binding: 1, resource: texture.createView() }
  ]
});

// 8. 编码并提交渲染命令(对应 WebGL 的 drawArrays)
const commandEncoder = device.createCommandEncoder();
const textureView = canvasContext.getCurrentTexture().createView();
const renderPassDescriptor = {
  colorAttachments: [{
    view: textureView,
    loadOp: 'clear',
    clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },
    storeOp: 'store'
  }]
};

const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
passEncoder.setPipeline(pipeline);
passEncoder.setVertexBuffer(0, vertexBuffer);
passEncoder.setBindGroup(0, bindGroup);
passEncoder.draw(6, 1, 0, 0); // 绘制6个顶点,对应 WebGL 的 drawArrays(TRIANGLES, 0, 6)
passEncoder.end();

device.queue.submit([commandEncoder.finish()]);

关键对应关系

  • WebGL 的 createTexture → WebGPU 的 device.createTexture,需明确指定纹理尺寸、格式和使用场景
  • WebGL 的 texParameteri → WebGPU 的 device.createSampler,采样参数独立于纹理,通过采样器配置
  • WebGL 的 texImage2D → WebGPU 的 device.queue.writeTexture,直接将 ImageData 的像素数据写入纹理
  • WebGL 的 drawArrays → WebGPU 需先编码渲染命令,设置管线、缓冲区、绑定组后调用 draw,最后提交队列

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 01:27:01