如何在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
相关产品推荐
相关产品推荐

