webgpu实现粒子颗粒化飘动动画效果代码
代码语言:html
所属分类:粒子
代码描述:webgpu实现粒子颗粒化飘动动画效果代码
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <style> html,body { margin: 0; overflow: hidden; } canvas { width: 100%; } </style> </head> <body translate="no"> <canvas></canvas> <script > console.clear(); if (!navigator.gpu) { console.error("WebGPU not supported in this browser."); throw new Error("WebGPU not supported."); } async function initWebGPU() { const canvas = document.querySelector('canvas'); const adapter = await navigator.gpu.requestAdapter(); const device = await adapter.requestDevice(); const context = canvas.getContext('webgpu'); // Resize canvas to fit the window const dpi = 2; const resizeCanvas = () => { canvas.width = window.innerWidth * dpi; canvas.height = window.innerHeight * dpi; }; resizeCanvas(); window.addEventListener('resize', resizeCanvas); // Configure the canvas context const format = navigator.gpu.getPreferredCanvasFormat(); context.configure({ device: device, format: format, alphaMode: 'premultiplied' }); return { device, context, format }; } function createRenderLoop(device, context, positionBuffer, velocityBuffer, timeBuffer) { const shaderModule = createShaderModule(device); const pipeline = createRenderPipeline(device, shaderModule); const { computePipeline, bindGroup } = initComputePass(device, positionBuffer, velocityBuffer, timeBuffer); const renderBindGroupLayout = pipeline.getBindGroupLayout(0); const renderBindGroup = device.createBindGroup({ layout: renderBindGroupLayout, entries: [{ binding: 0, resource: { buffer: positionBuffer } }] }); let startTime = performance.now(); const render = () => { const elapsedTime = (performance.now() - startTime) * .0001; device.queue.writeBuffer(timeBuffer, 0, new Float32Array([elapsedTime])); const commandEncoder = device.createCommandEncoder(); // Compute Pass const computePass = commandEncoder.beginComputePass(); computePass.setPipeline(computePipeline); computePass.setBindGroup(0, bindGroup); computePass.dispatchWorkgroups(Math.ceil(NUM_PARTICLES / 64)); computePass.end(); // Render Pass const textureView = context.getCurrentTexture().createView(); const renderPassDescriptor = { colorAttachments: [{ view: textureView, clearValue: { r: 0, g: 0, b: 0, a: 1 }, loadOp: 'clear', storeOp: 'store' }] }; const renderPass = commandEncoder.beginRenderPass(renderPassDescriptor); renderPass.setPipeline(pipeline); renderPass.setBindGroup(0, renderBindGroup); renderPass.draw(NUM_PARTICLES); renderPass.end(); device.queue.submit([commandEncoder.finish()]); requestAnimationFrame(render); }; render(); } const NUM_PARTICLES = 2000000; function generateParticleData() { const positions = new Float32Array(NUM_PARTICLES * 3); const velocities = new Float32Array(NUM_PARTICLES * 3); for (let i = 0; i < NUM_PARTICLES; i++) { // Random position in a cube [-1, 1] positions[i * 3 + 0] = Math.random() * 1 - .5; // x positions[i * 3 + 1] = Math.random() * 1 - .5; // y positions[i * 3 + 2] = Math.random() * 1 - .5; // z // Random velocity in range [-0.1, 0.1] velocities[i * 3 + 0] = Math.random() * 0.2 - 0.1; // vx velocities[i * 3 + 1] = Math.random() * 0.2 - 0.1; // vy velocities[i * 3 + 2] = Math.random() * 0.2 - 0.1; // vz } return { positions, velocities }; } function createParticleBuffers(device, positions, velocities) { // Create position buffer const positionBuffer = device.createBuffer({ size: positions.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.STORAGE, mappedAtCreation: true }); new Float32Array(positionBuffer.getMappedRange()).set(positions); positionBuffer.unmap(); // Create velocity buffer const velocityBuffer = device.createBuffer({ size: velocities.byteLength, usage: GPUBufferUsage.STORAGE, mappedAtCreation: true }); new Float32Array(velocityBuffer.getMappedRange()).set(velocities); velocityBuffer.unmap(); return { positionBuffer, velocityBuffer }; } async function initParticleSystem(device) { const { positions, velocities } = generateParticleData(); const buffers = createParticleBuffers(device, positions, velocities); return buffers; } const shaderCode = ` @group(0) @binding(0) var<storage, read> particlePositions: array<vec3<f32>>; struct VertexOutput { @builtin(position) position: vec4<f32>, @location(0) pos: vec3<f32> } @vertex fn vmain(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput { var opt: VertexOutput; let position = particlePositions[vertexIndex]; opt.position = vec4<f32>(position, 1.0); opt.pos = position; return opt; } @fragment fn fmain(input: VertexOutput) -> @location(0) vec4<f32> { return vec4<f32>(1, 1, 1, .5+.5*input.pos.z); } `; function createShaderModule(device) { return device.createShaderModule({ code: shaderCode }); } function createRenderPipeline(device, shaderModule) { return device.createRenderPipeline({ layout: 'auto', vertex: { module: shaderModule, entryPoint: 'vmain' // Vertex shader entry point }, fragment: { module: shaderModule, entryPoint: 'fmain', // Fragment shader entry point targets: [{ format: 'bgra8unorm', blend: { color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operations: 'add' }, alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operations: 'add' } } }] }, primitive: { topology: 'point-list' // Render particles as points } }); } const computeShaderCode = ` @group(0) @binding(0) var<storage, read_write> particlePositions: array<vec3<f32>>; @group(0) @binding(1) var<storage, read_write> particleVelocities: array<vec3<f32>>; @group(0) @binding(2) var<uniform> time: f32; // https://www.pcg-random.org/ fn pcg(n: u32) -> u32 { var h = n * 747796405u + 2891336453u; h = ((h >> ((h >> 28u) + 4u)) ^ h) * 277803737u; return (h >> 22u) ^ .........完整代码请登录后点击上方下载按钮下载查看
网友评论0