Google codelab WebGPU入门教程源码<6> - 使用计算着色器实现计算元胞自动机之生命游戏模拟过程(源码)

本文主要是介绍Google codelab WebGPU入门教程源码<6> - 使用计算着色器实现计算元胞自动机之生命游戏模拟过程(源码),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

对应的教程文章: 

https://codelabs.developers.google.com/your-first-webgpu-app?hl=zh-cn#7

对应的源码执行效果:

对应的教程源码: 

此处源码和教程本身提供的部分代码可能存在一点差异。点击画面,切换效果。

class Color4 {r: number;g: number;b: number;a: number;constructor(pr = 1.0, pg = 1.0, pb = 1.0, pa = 1.0) {this.r = pr;this.g = pg;this.b = pb;this.a = pa;}
}export class WGPUSimulation {private mRVertices: Float32Array = null;private mRPipeline: any | null = null;private mRSimulationPipeline: any | null = null;private mVtxBuffer: any | null = null;private mCanvasFormat: any | null = null;private mWGPUDevice: any | null = null;private mWGPUContext: any | null = null;private mUniformBindGroups: any | null = null;private mGridSize = 32;private mShdWorkGroupSize = 8;constructor() {}initialize(): void {const canvas = document.createElement("canvas");canvas.width = 512;canvas.height = 512;document.body.appendChild(canvas);console.log("ready init webgpu ...");this.initWebGPU(canvas).then(() => {console.log("webgpu initialization finish ...");this.updateWGPUCanvas();});document.onmousedown = (evt):void => {this.updateWGPUCanvas( new Color4(0.05, 0.05, 0.1) );}}private mUniformObj: any = {uniformArray: null, uniformBuffer: null};private createStorage(device: any): any {// Create an array representing the active state of each cell.const cellStateArray = new Uint32Array(this.mGridSize * this.mGridSize);// Create two storage buffers to hold the cell state.const cellStateStorage = [device.createBuffer({label: "Cell State A",size: cellStateArray.byteLength,usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,}),device.createBuffer({label: "Cell State B",size: cellStateArray.byteLength,usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,})];// Mark every third cell of the first grid as active.for (let i = 0; i < cellStateArray.length; i+=3) {cellStateArray[i] = 1;}device.queue.writeBuffer(cellStateStorage[0], 0, cellStateArray);// Mark every other cell of the second grid as active.for (let i = 0; i < cellStateArray.length; i++) {cellStateArray[i] = i % 2;}device.queue.writeBuffer(cellStateStorage[1], 0, cellStateArray);return cellStateStorage;}private createUniform(device: any): any {// Create a uniform buffer that describes the grid.const uniformArray = new Float32Array([this.mGridSize, this.mGridSize]);const uniformBuffer = device.createBuffer({label: "Grid Uniforms",size: uniformArray.byteLength,usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,});device.queue.writeBuffer(uniformBuffer, 0, uniformArray);const cellStateStorage = this.createStorage(device);// Create the bind group layout and pipeline layout.const bindGroupLayout = device.createBindGroupLayout({label: "Cell Bind Group Layout",entries: [{binding: 0,visibility: GPUShaderStage.FRAGMENT | GPUShaderStage.VERTEX | GPUShaderStage.COMPUTE,buffer: {} // Grid uniform buffer}, {binding: 1,visibility: GPUShaderStage.VERTEX | GPUShaderStage.COMPUTE,buffer: { type: "read-only-storage"} // Cell state input buffer}, {binding: 2,visibility: GPUShaderStage.COMPUTE,buffer: { type: "storage"} // Cell state output buffer}]});const bindGroups = [device.createBindGroup({label: "Cell renderer bind group A",layout: bindGroupLayout,entries: [{binding: 0,resource: { buffer: uniformBuffer }}, {binding: 1,resource: { buffer: cellStateStorage[0] }}, {binding: 2,resource: { buffer: cellStateStorage[1] }}],}),device.createBindGroup({label: "Cell renderer bind group B",layout: bindGroupLayout,entries: [{binding: 0,resource: { buffer: uniformBuffer }}, {binding: 1,resource: { buffer: cellStateStorage[1] }}, {binding: 2,resource: { buffer: cellStateStorage[0] }}],})];this.mUniformBindGroups = bindGroups;const obj = this.mUniformObj;obj.uniformArray = uniformArray;obj.uniformBuffer = uniformBuffer;return bindGroupLayout;}private mStep = 0;private createComputeShader(device: any): any {let sgs = this.mShdWorkGroupSize;// Create the compute shader that will process the simulation.const simulationShaderModule = device.createShaderModule({label: "Game of Life simulation shader",code: `@group(0) @binding(0) var<uniform> grid: vec2f;@group(0) @binding(1) var<storage> cellStateIn: array<u32>;@group(0) @binding(2) var<storage, read_write> cellStateOut: array<u32>;fn cellIndex(cell: vec2u) -> u32 {return cell.y * u32(grid.x) + cell.x;}@compute @workgroup_size(${sgs}, ${sgs})fn computeMain(@builtin(global_invocation_id) cell: vec3u) {if (cellStateIn[cellIndex(cell.xy)] == 1) {cellStateOut[cellIndex(cell.xy)] = 0;} else {cellStateOut[cellIndex(cell.xy)] = 1;}}`});return simulationShaderModule;}private createRectGeometryData(device: any, pass: any, computePass: any): void {let vertices = this.mRVertices;let vertexBuffer = this.mVtxBuffer;let cellPipeline = this.mRPipeline;let simulationPipeline = this.mRSimulationPipeline;if(!cellPipeline) {let hsize = 0.8;vertices = new Float32Array([//   X,    Y,-hsize, -hsize, // Triangle 1 (Blue)hsize, -hsize,hsize,  hsize,-hsize, -hsize, // Triangle 2 (Red)hsize,  hsize,-hsize,  hsize,]);vertexBuffer = device.createBuffer({label: "Cell vertices",size: vertices.byteLength,usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,});device.queue.writeBuffer(vertexBuffer, /*bufferOffset=*/0, vertices);const vertexBufferLayout = {arrayStride: 8,attributes: [{format: "float32x2",offset: 0,shaderLocation: 0, // Position, see vertex shader}],};const shaderCodes = `struct VertexInput {@location(0) pos: vec2f,@builtin(instance_index) instance: u32,};struct VertexOutput {@builtin(position) pos: vec4f,@location(0) cell: vec2f,};@group(0) @binding(0) var<uniform> grid: vec2f;@group(0) @binding(1) var<storage> cellState: array<u32>;@vertexfn vertexMain(input: VertexInput) -> VertexOutput  {let i = f32(input.instance);let cell = vec2f(i % grid.x, floor(i / grid.x));let cellOffset = cell / grid * 2;let state = f32(cellState[input.instance]);let gridPos = (input.pos * state + 1) / grid - 1 + cellOffset;var output: VertexOutput;output.pos = vec4f(gridPos, 0, 1);output.cell = cell;return output;}@fragmentfn fragmentMain(input: VertexOutput) -> @location(0) vec4f {// return vec4f(input.cell, 0, 1);let c = input.cell/grid;return vec4f(c, 1.0 - c.x, 1);}`;const bindGroupLayout = this.createUniform(device);const pipelineLayout = device.createPipelineLayout({label: "Cell Pipeline Layout",bindGroupLayouts: [ bindGroupLayout ],});const cellShaderModule = device.createShaderModule({label: "Cell shader",code: shaderCodes});cellPipeline = device.createRenderPipeline({label: "Cell pipeline",layout: pipelineLayout,vertex: {module: cellShaderModule,entryPoint: "vertexMain",buffers: [vertexBufferLayout]},fragment: {module: cellShaderModule,entryPoint: "fragmentMain",targets: [{format: this.mCanvasFormat}]},});const simulationShaderModule = this.createComputeShader( device );// Create a compute pipeline that updates the game state.simulationPipeline = device.createComputePipeline({label: "Simulation pipeline",layout: pipelineLayout,compute: {module: simulationShaderModule,entryPoint: "computeMain",}});this.mRVertices = vertices;this.mVtxBuffer = vertexBuffer;this.mRPipeline = cellPipeline;this.mRSimulationPipeline = simulationPipeline;}const bindGroups = this.mUniformBindGroups;computePass.setPipeline(simulationPipeline),computePass.setBindGroup(0, bindGroups[this.mStep % 2]);const workgroupCount = Math.ceil(this.mGridSize / this.mShdWorkGroupSize);computePass.dispatchWorkgroups(workgroupCount, workgroupCount);pass.setPipeline(cellPipeline);pass.setVertexBuffer(0, vertexBuffer);// pass.setBindGroup(0, this.mUniformBindGroup);pass.setBindGroup(0, bindGroups[this.mStep % 2]);pass.draw(vertices.length / 2, this.mGridSize * this.mGridSize);this.mStep ++;}private updateWGPUCanvas(clearColor: Color4 = null): void {clearColor = clearColor ? clearColor : new Color4(0.05, 0.05, 0.1);const device = this.mWGPUDevice;const context = this.mWGPUContext;const rpassParam = {colorAttachments: [{clearValue: clearColor,// clearValue: [0.3,0.7,0.5,1.0], // yesview: context.getCurrentTexture().createView(),loadOp: "clear",storeOp: "store"}]};const encoder = device.createCommandEncoder();const pass = encoder.beginRenderPass( rpassParam );const computeEncoder = device.createCommandEncoder();const computePass = computeEncoder.beginComputePass()this.createRectGeometryData(device, pass, computePass);pass.end();computePass.end();device.queue.submit([ encoder.finish() ]);device.queue.submit([ computeEncoder.finish() ]);}private async initWebGPU(canvas: HTMLCanvasElement) {const gpu = (navigator as any).gpu;if (gpu) {console.log("WebGPU supported on this browser.");const adapter = await gpu.requestAdapter();if (adapter) {console.log("Appropriate GPUAdapter found.");const device = await adapter.requestDevice();if (device) {this.mWGPUDevice = device;console.log("Appropriate GPUDevice found.");const context = canvas.getContext("webgpu") as any;const canvasFormat = gpu.getPreferredCanvasFormat();this.mWGPUContext = context;this.mCanvasFormat = canvasFormat;console.log("canvasFormat: ", canvasFormat);context.configure({device: device,format: canvasFormat,alphaMode: "premultiplied"});} else {throw new Error("No appropriate GPUDevice found.");}} else {throw new Error("No appropriate GPUAdapter found.");}} else {throw new Error("WebGPU not supported on this browser.");}}run(): void {}
}

这篇关于Google codelab WebGPU入门教程源码<6> - 使用计算着色器实现计算元胞自动机之生命游戏模拟过程(源码)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/428216

相关文章

Spring boot整合dubbo+zookeeper的详细过程

《Springboot整合dubbo+zookeeper的详细过程》本文讲解SpringBoot整合Dubbo与Zookeeper实现API、Provider、Consumer模式,包含依赖配置、... 目录Spring boot整合dubbo+zookeeper1.创建父工程2.父工程引入依赖3.创建ap

Linux下进程的CPU配置与线程绑定过程

《Linux下进程的CPU配置与线程绑定过程》本文介绍Linux系统中基于进程和线程的CPU配置方法,通过taskset命令和pthread库调整亲和力,将进程/线程绑定到特定CPU核心以优化资源分配... 目录1 基于进程的CPU配置1.1 对CPU亲和力的配置1.2 绑定进程到指定CPU核上运行2 基于

使用Python删除Excel中的行列和单元格示例详解

《使用Python删除Excel中的行列和单元格示例详解》在处理Excel数据时,删除不需要的行、列或单元格是一项常见且必要的操作,本文将使用Python脚本实现对Excel表格的高效自动化处理,感兴... 目录开发环境准备使用 python 删除 Excphpel 表格中的行删除特定行删除空白行删除含指定

深入理解Go语言中二维切片的使用

《深入理解Go语言中二维切片的使用》本文深入讲解了Go语言中二维切片的概念与应用,用于表示矩阵、表格等二维数据结构,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录引言二维切片的基本概念定义创建二维切片二维切片的操作访问元素修改元素遍历二维切片二维切片的动态调整追加行动态

Linux下删除乱码文件和目录的实现方式

《Linux下删除乱码文件和目录的实现方式》:本文主要介绍Linux下删除乱码文件和目录的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux下删除乱码文件和目录方法1方法2总结Linux下删除乱码文件和目录方法1使用ls -i命令找到文件或目录

prometheus如何使用pushgateway监控网路丢包

《prometheus如何使用pushgateway监控网路丢包》:本文主要介绍prometheus如何使用pushgateway监控网路丢包问题,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录监控网路丢包脚本数据图表总结监控网路丢包脚本[root@gtcq-gt-monitor-prome

SpringBoot+EasyExcel实现自定义复杂样式导入导出

《SpringBoot+EasyExcel实现自定义复杂样式导入导出》这篇文章主要为大家详细介绍了SpringBoot如何结果EasyExcel实现自定义复杂样式导入导出功能,文中的示例代码讲解详细,... 目录安装处理自定义导出复杂场景1、列不固定,动态列2、动态下拉3、自定义锁定行/列,添加密码4、合并

mybatis执行insert返回id实现详解

《mybatis执行insert返回id实现详解》MyBatis插入操作默认返回受影响行数,需通过useGeneratedKeys+keyProperty或selectKey获取主键ID,确保主键为自... 目录 两种方式获取自增 ID:1. ​​useGeneratedKeys+keyProperty(推

Spring Boot集成Druid实现数据源管理与监控的详细步骤

《SpringBoot集成Druid实现数据源管理与监控的详细步骤》本文介绍如何在SpringBoot项目中集成Druid数据库连接池,包括环境搭建、Maven依赖配置、SpringBoot配置文件... 目录1. 引言1.1 环境准备1.2 Druid介绍2. 配置Druid连接池3. 查看Druid监控

Python通用唯一标识符模块uuid使用案例详解

《Python通用唯一标识符模块uuid使用案例详解》Pythonuuid模块用于生成128位全局唯一标识符,支持UUID1-5版本,适用于分布式系统、数据库主键等场景,需注意隐私、碰撞概率及存储优... 目录简介核心功能1. UUID版本2. UUID属性3. 命名空间使用场景1. 生成唯一标识符2. 数