WebGPU 培训
WebGPU是什么? 它是一个在浏览器中实现低级图形渲染的API,可以让Web开发人员利用GPU提高渲染性能。WebGPU支持多种GPU,并允许Web应用程序通过JavaScript,WebAssembly或HLSL代码与GPU通信。
WebGPU可与WebGL和WebXR等其他Web图形API配合使用,但它是更先进和更灵活的API,提供了更多的控制权和更好的性能,从而更适合处理复杂和大型3D场景。
要使用WebGPU,需要使用最新的Web浏览器(如Chrome)和支持WebGPU的GPU。
const adapter = await navigator.gpu.requestAdapter(); const device = await adapter.requestDevice();
使用WebGPU,我们需要获取GPU的适配器(adapter)和设备(device),并将其存储在变量中,以便我们可以在GPU上执行操作。
实用WebGPU
1. 使用WebGPU渲染三角形
在使用WebGPU渲染三角形之前,需要进行一些准备工作。我们需要编写代码来创建窗口、初始化WebGPU并定义渲染器所需的常量和资源。在这里,我们使用了GLSL作为着色语言。
let vertexShader = ` #version 450 layout(location = 0) in vec3 pos; void main() { gl_Position = vec4(pos, 1.0); } `; let fragmentShader = ` #version 450 layout(location = 0) out vec4 outColor; void main() { outColor = vec4(1.0, 1.0, 1.0, 1.0); } `; async function main() { // Step 1: Create a window to render into. const canvas = document.createElement('canvas'); document.body.appendChild(canvas); canvas.width = window.innerWidth; canvas.height = window.innerHeight; // Step 2: Initialize WebGPU and create the "context". const adapter = await navigator.gpu.requestAdapter(); const device = await adapter.requestDevice(); const context = canvas.getContext('gpupresent'); // Step 3: Define the shader modules (i.e. the shader code). const shaderModule = device.createShaderModule({ code: vertexShader }); const fragModule = device.createShaderModule({ code: fragmentShader }); // Step 4: Define the pipeline layout. This specifies the vertex input // format, and any other inputs we will be using in our shaders. const layout = device.createPipelineLayout({ bindGroupLayouts: [] }); // Step 5: Define the render pipeline. This specifies the shaders to use, // as well as the pipeline layout from step 4. const pipeline = device.createRenderPipeline({ layout, vertex: { module: shaderModule, entryPoint: 'main', buffers: [{ arrayStride: 12, attributes: [{ shaderLocation: 0, offset: 0, format: 'float3' }] }] }, fragment: { module: fragModule, entryPoint: 'main', targets: [{ format: 'rgba8unorm' }] }, primitive: { topology: 'triangle-list', stripIndexFormat: undefined, frontFace: 'ccw', cullMode: 'none', clampDepth: false }, depthStencil: { format: undefined, depthWriteEnabled: false, depthCompare: 'always', stencilFront: { compare: 'always', failOp: 'keep', passOp: 'keep', depthFailOp: 'keep' }, stencilBack: { compare: 'always', failOp: 'keep', passOp: 'keep', depthFailOp: 'keep' }, stencilReadMask: 0xff, stencilWriteMask: 0xff } }); // Step 6: Define the uniform buffer. const uniformBuffer = device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST }); const uniformBindGroup = device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [{ binding: 0, resource: { buffer: uniformBuffer } }] }); // Step 7: Define the render loop. function update() { const renderPassDescriptor = { colorAttachments: [{ attachment: context.getCurrentTexture().createView(), loadValue: [0, 0, 0, 1], storeOp: 'store' }] }; const commandEncoder = device.createCommandEncoder(); const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor); passEncoder.setPipeline(pipeline); passEncoder.setBindGroup(0, uniformBindGroup); passEncoder.draw(3, 1, 0, 0); passEncoder.endPass(); device.queue.submit([commandEncoder.finish()]); requestAnimationFrame(update); } requestAnimationFrame(update); } main();
WebGPU React
使用WebGPU与React结合使用可以快速创建高性能的3D应用程序。下面的示例代码演示了如何在React中使用Jaspr和WebGPU来渲染一个简单的3D场景。
1. 在React中使用WebGPU渲染3D场景
import * as THREE from 'three'; import { useResource, useFrame, Canvas, } from 'react-three-fiber'; import Jaspr from 'jaspr'; function TriangleMesh(props) { const geometry = new THREE.Geometry(); geometry.vertices.push( new THREE.Vector3(-1, 0, 0), new THREE.Vector3(0, 1, 0), new THREE.Vector3(1, 0, 0), ); geometry.faces.push(new THREE.Face3(0, 1, 2)); const [meshRef] = useResource(); useFrame(({ clock }) => { meshRef.rotation.x = clock.getElapsedTime() * 0.5; meshRef.rotation.y = clock.getElapsedTime() * 0.8; }); return (); } function Scene() { return ( v.toArray())} itemSize={3} /> ); } function App() { return ( ); } export default App;
2. WebGPU 渲染动画
下面是一个简单的示例代码,演示如何在WebGPU上渲染动画。
async function main() { // Step 1: Create a window to render into. const canvas = document.createElement('canvas'); document.body.appendChild(canvas); canvas.width = window.innerWidth; canvas.height = window.innerHeight; // Step 2: Initialize WebGPU and create the "context". const adapter = await navigator.gpu.requestAdapter(); const device = await adapter.requestDevice(); const context = canvas.getContext('gpupresent'); // Step 3: Define the shader modules (i.e. the shader code). const shaderModule = device.createShaderModule({ code: vertexShader }); const fragModule = device.createShaderModule({ code: fragmentShader }); // Step 4: Define the pipeline layout. const layout = device.createPipelineLayout({ bindGroupLayouts: [] }); // Step 5: Define the render pipeline. const pipeline = device.createRenderPipeline({ layout, vertex: { module: shaderModule, entryPoint: 'main', buffers: [{ arrayStride: 12, attributes: [{ shaderLocation: 0, offset: 0, format: 'float3' }] }] }, fragment: { module: fragModule, entryPoint: 'main', targets: [{ format: 'rgba8unorm' }] }, primitive: { topology: 'triangle-list', stripIndexFormat: undefined, frontFace: 'ccw', cullMode: 'none', clampDepth: false }, depthStencil: { format: undefined, depthWriteEnabled: false, depthCompare: 'always', stencilFront: { compare: 'always', failOp: 'keep', passOp: 'keep', depthFailOp: 'keep' }, stencilBack: { compare: 'always', failOp: 'keep', passOp: 'keep', depthFailOp: 'keep' }, stencilReadMask: 0xff, stencilWriteMask: 0xff } }); // Step 6: Define the uniform buffer. const uniformBuffer = device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST }); const uniformBindGroup = device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [{ binding: 0, resource: { buffer: uniformBuffer } }] }); // Step 7: Define the render loop. let time = 0; function update() { const t = performance.now() / 1000.0; const delta = t - time; time = t; const renderPassDescriptor = { colorAttachments: [{ attachment: context.getCurrentTexture().createView(), loadValue: [0, 0, 0, 1], storeOp: 'store' }] }; const commandEncoder = device.createCommandEncoder(); const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor); passEncoder.setPipeline(pipeline); passEncoder.setBindGroup(0, uniformBindGroup); passEncoder.draw(3, 1, 0, 0); passEncoder.endPass(); device.queue.submit([commandEncoder.finish()]); requestAnimationFrame(update); } update(); } main();