当前位置: 首页 > news >正文

轻量封装WebGPU渲染系统示例<20>- 美化一下元胞自动机之生命游戏(源码)

当前示例源码github地址:

https://github.com/vilyLei/voxwebgpu/blob/feature/rendering/src/voxgpu/sample/GameOfLifePretty.ts

系统特性:

1. 用户态与系统态隔离。

2. 高频调用与低频调用隔离。

3. 面向用户的易用性封装。

4. 渲染数据(内外部相关资源)和渲染机制分离。

5. 用户操作和渲染系统调度并行机制。

6. 数据/语义驱动。

7. 异步并行的场景/模型载入。

8. computing与rendering用法机制一致性。

        1). 构造过程一致性。

        2). 启用过程一致性。

        3). 自动兼容到material多pass以及material graph机制中。

当前示例运行效果:

WGSL顶点与片段shader:

struct VertexInput {@location(0) pos: vec3f,@location(1) uv : vec2f,@builtin(instance_index) instance: u32
};struct VertexOutput {@builtin(position) pos: vec4f,@location(0) cell: vec2f,@location(1) uv: vec2f,@location(2) instance: f32,
};
@group(0) @binding(0) var<uniform> grid: vec2f;
@group(0) @binding(1) var<storage> cellState: array<u32>;
@group(0) @binding(2) var<storage> lifeState: array<f32>;
@vertex
fn vertMain(input: VertexInput) -> VertexOutput {let i = f32(input.instance);let cell = vec2f(i % grid.x, floor(i / grid.x));let cellOffset = cell / grid * 2.0;var state = f32(cellState[input.instance]);let gridPos = (input.pos.xy * state + 1.0) / grid - 1.0 + cellOffset;var output: VertexOutput;output.pos = vec4f(gridPos, 0.0, 1.0);output.cell = cell;output.uv = input.uv;output.instance = i;return output;
}@fragment
fn fragMain(input: VertexOutput) -> @location(0) vec4f {let c = input.cell / grid;var dis = length(input.uv - vec2<f32>(0.5, 0.5));dis = min(dis/0.15, 1.0);let i = u32(input.instance);var f = clamp((lifeState[i])/100.0, 0.0005, 1.0);dis = (1.0 - pow(dis, 100.0)) * (1.0 - f) + f;var c3 = vec3f(c, (1.0 - c.x) * (1.0 - f) + f) * dis;return vec4f(c3, 1.0);
}

此示例基于此渲染系统实现,当前示例TypeScript源码如下:

const gridSize = 64;
const shdWorkGroupSize = 8;const compShdCode = `
@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>;
@group(0) @binding(3) var<storage, read_write> lifeState: array<f32>;fn cellIndex(cell: vec2u) -> u32 {return (cell.y % u32(grid.y)) * u32(grid.x) +(cell.x % u32(grid.x));
}fn cellActive(x: u32, y: u32) -> u32 {return cellStateIn[cellIndex(vec2(x, y))];
}@compute @workgroup_size(${shdWorkGroupSize}, ${shdWorkGroupSize})
fn compMain(@builtin(global_invocation_id) cell: vec3u) {// Determine how many active neighbors this cell has.let activeNeighbors = cellActive(cell.x+1, 		cell.y+1) +cellActive(cell.x+1, 	cell.y) +cellActive(cell.x+1, 	cell.y-1) +cellActive(cell.x, 		cell.y-1) +cellActive(cell.x-1, 	cell.y-1) +cellActive(cell.x-1, 	cell.y) +cellActive(cell.x-1, 	cell.y+1) +cellActive(cell.x, 		cell.y+1);let i = cellIndex(cell.xy);// Conway's game of life rules:switch activeNeighbors {case 2: { // Active cells with 2 neighbors stay active.cellStateOut[i] = cellStateIn[i];if(cellStateOut[i] > 0) {lifeState[i] += 0.5;} else {lifeState[i] -= 0.5;}}case 3: { // Cells with 3 neighbors become or stay active.cellStateOut[i] = 1;lifeState[i] += 0.5;}default: { // Cells with < 2 or > 3 neighbors become inactive.cellStateOut[i] = 0;lifeState[i] = 0.01;}}if(lifeState[i] < 0.01) { lifeState[i] = 0.01; }
}`;
export class GameOfLifePretty {private mRscene = new RendererScene();initialize(): void {console.log("GameOfLifePretty::initialize() ...");const rc = this.mRscene;rc.initialize();this.initScene();}private createUniformValues(): { ufvs0: WGRUniformValue[]; ufvs1: WGRUniformValue[] }[] {const gridsSizesArray = new Float32Array([gridSize, gridSize]);const cellStateArray0 = new Uint32Array(gridSize * gridSize);for (let i = 0; i < cellStateArray0.length; i++) {cellStateArray0[i] = Math.random() > 0.8 ? 1 : 0;}const cellStateArray1 = new Uint32Array(gridSize * gridSize);for (let i = 0; i < cellStateArray1.length; i++) {cellStateArray1[i] = i % 2;}const lifeStateArray3 = new Float32Array(gridSize * gridSize);for (let i = 0; i < lifeStateArray3.length; i++) {lifeStateArray3[i] = 0.01;}let shared = true;let sharedData0 = { data: cellStateArray0 };let sharedData1 = { data: cellStateArray1 };let sharedData3 = { data: lifeStateArray3 };const v0 = new WGRUniformValue({ data: gridsSizesArray, stride: 2, shared });v0.toVisibleAll();// build rendering uniformsconst va1 = new WGRStorageValue({ sharedData: sharedData0, stride: 1, shared }).toVisibleVertComp();const vb1 = new WGRStorageValue({ sharedData: sharedData1, stride: 1, shared }).toVisibleVertComp();const vc1 = new WGRStorageValue({ sharedData: sharedData3, stride: 1, shared }).toVisibleAll();// build computing uniformsconst compva1 = new WGRStorageValue({ sharedData: sharedData0, stride: 1, shared }).toVisibleVertComp();const compva2 = new WGRStorageValue({ sharedData: sharedData1, stride: 1, shared }).toVisibleComp();compva2.toBufferForStorage();const compvb1 = new WGRStorageValue({ sharedData: sharedData1, stride: 1, shared }).toVisibleVertComp();const compvb2 = new WGRStorageValue({ sharedData: sharedData0, stride: 1, shared }).toVisibleComp();compvb2.toBufferForStorage();const compv3 = new WGRStorageValue({ sharedData: sharedData3, stride: 1, shared }).toVisibleComp();compv3.toBufferForStorage();return [{ ufvs0: [v0, va1, vc1], ufvs1: [v0, vb1, vc1] },{ ufvs0: [v0, compva1, compva2, compv3], ufvs1: [v0, compvb1, compvb2, compv3] }];}private mEntity: FixScreenPlaneEntity;private mStep = 0;private createMaterial(shaderCodeSrc: WGRShderSrcType, uniformValues: WGRUniformValue[], shadinguuid: string, instanceCount: number): WGMaterial {return new WGMaterial({shadinguuid,shaderCodeSrc,instanceCount,uniformValues});}private createCompMaterial(shaderCodeSrc: WGRShderSrcType, uniformValues: WGRUniformValue[], shadinguuid: string, workgroupCount = 2): WGCompMaterial {return new WGCompMaterial({shadinguuid,shaderCodeSrc,uniformValues}).setWorkcounts(workgroupCount, workgroupCount);}private initScene(): void {const rc = this.mRscene;const ufvsObjs = this.createUniformValues();const instanceCount = gridSize * gridSize;const workgroupCount = Math.ceil(gridSize / shdWorkGroupSize);let shaderSrc = {shaderSrc: {code: shaderWGSL,uuid: "shader-gameOfLife",vertEntryPoint: "vertMain",fragEntryPoint: "fragMain"}};let compShaderSrc = {compShaderSrc: {code: compShdCode,uuid: "shader-computing",compEntryPoint: "compMain"}};const materials: WGMaterial[] = [// build ping-pong rendering processthis.createMaterial(shaderSrc, ufvsObjs[0].ufvs0, "rshd0", instanceCount),this.createMaterial(shaderSrc, ufvsObjs[0].ufvs1, "rshd1", instanceCount),// build ping-pong computing processthis.createCompMaterial(compShaderSrc, ufvsObjs[1].ufvs1, "compshd0", workgroupCount),this.createCompMaterial(compShaderSrc, ufvsObjs[1].ufvs0, "compshd1", workgroupCount),];let entity = new FixScreenPlaneEntity({x: -0.8, y: -0.8, width: 1.6, height: 1.6,materials});rc.addEntity(entity);materials[0].visible = false;materials[2].visible = false;this.mEntity = entity;}private mFrameDelay = 3;run(): void {let rendering = this.mEntity.isRendering();if (rendering) {if (this.mFrameDelay > 0) {this.mFrameDelay--;return;}this.mFrameDelay = 3;const ms = this.mEntity.materials;for (let i = 0; i < ms.length; i++) {ms[i].visible = (this.mStep % 2 + i) % 2 == 0;}this.mStep++;}this.mRscene.run(rendering);}
}

相关文章:

轻量封装WebGPU渲染系统示例<20>- 美化一下元胞自动机之生命游戏(源码)

当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/rendering/src/voxgpu/sample/GameOfLifePretty.ts 系统特性: 1. 用户态与系统态隔离。 2. 高频调用与低频调用隔离。 3. 面向用户的易用性封装。 4. 渲染数据(内外部相关资源)和渲染机制分离…...

Nodejs的安装以及配置(node-v12.16.1-x64.msi)

Nodejs的安装以及配置 1、安装 node-v12.16.1-x64.msi点击安装&#xff0c;注意以下步骤 本文设置nodejs的安装的路径&#xff1a;D:\soft\nodejs 继续点击next&#xff0c;选中Add to PATH &#xff0c;旁边的英文告诉我们会把 环境变量 给我们配置好 当然也可以只选择 Nod…...

03【保姆级】-GO语言变量和数据类型和相互转换

03【保姆级】-GO语言变量和数据类型 一、变量1.1 变量的定义&#xff1a;1.2 变量的声明、初始化、赋值1.3 变量使用的注意事项 插播-关于fmt.Printf格式打印%的作用二、 变量的数据类型2.1整数的基本类型2.1.1 有符号类型 int8/16/32/642.1.2 无符号类型 int8/16/32/642.1.3 整…...

mermaid学习第一天/更改主题颜色和边框颜色/《需求解释流程图》

mermaid 在线官网&#xff1a; https://mermaid-js.github.io/ 在线学习文件&#xff1a; https://mermaid.js.org/syntax/quadrantChart.html 1、今天主要是想做需求解释的流程图&#xff0c;又不想自己画&#xff0c;就用了&#xff0c;框框不能直接进行全局配置&#xff0…...

SAP MASS增加PR字段-删除标识

MASS->BUS2105->发现没有找到PR删除标识字段 SAP MASS增加PR字段-删除标识 1.tcode:MASSOBJ 选中BUS2105 点“应用程序表” 点“字段列表” 2.选中一行进行参考 3.修改字段为删除标识 LOEKZ&#xff0c;保存即可。 4.然后MASS操作&#xff0c;批量设置删除标识&…...

【手把手教你】训练YOLOv8分割模型

1.下载文件 在github上下载YOLOV8模型的文件&#xff0c;搜索yolov8&#xff0c;star最多这个就是 2. 准备环境 环境要求python>3.8&#xff0c;PyTorch>1.8&#xff0c;自行安装ptyorch环境即可 2. 制作数据集 制作数据集&#xff0c;需要使用labelme这个包&#…...

物料主数据增强屏幕绘制器DUMP

问题描述 在做完物料主数据增强后&#xff0c;配置和代码传Q&#xff0c;在Q进入增强屏幕绘制器报错。 错误 CALLBACK_REJECTED_BY_WHITELIST RFC callback call rejected by positive list An RFC callback has been prevented due to no corresponding positive list en…...

vue 实现在线预览Excel-LuckyExcel/LuckySheet实现方案

一、准备工作 1. npm安装 luckyexcel npm i -D luckyexcel 2.引入luckysheet 注意&#xff1a;引入luckysheet&#xff0c;只能通过CDN或者直接引入静态资源的形式&#xff0c;不能npm install。 个人建议直接下载资源引入。我给你们提供一个下载资源的地址&#xff1a; …...

AIGPT重大升级,界面重新设计,功能更加饱满,用户体验升级

AIGPT AIGPT是一款功能强大的人工智能技术处理软件&#xff0c;不但拥有其他模型处理文本认知的能力还有AI绘画模型、拥有自身的插件库。 我们都知道使用ChatGPT是需要账号以及使用魔法的&#xff0c;实现其中的某一项对我们一般的初学者来说都是一次巨大的挑战&#xff0c;但…...

Web逆向-mtgsig1.2简单分析

{"a1": "1.2", # 加密版本"a2": new Date().valueOf() - serverTimeDiff, # 加密过程中用到的时间戳. 这次服主变坏了, 时间戳需要减去一个 serverTimeDiff(见a3) ! "a3": "这是把xxx信息加密后提交给服务器, 服主…...

【蓝桥杯省赛真题41】Scratch电脑开关机 蓝桥杯少儿编程scratch图形化编程 蓝桥杯省赛真题讲解

目录 scratch电脑开关机 一、题目要求 编程实现 二、案例分析 1、角色分析...

第10章 Java常用类

目录 内容说明 章节内容 一、Object类 二、String类和StringBuffer类 三、Math类和Random类...

Android 11 getPackageManager().getPackageInfo 返回null

Android11 之后&#xff0c; 在查找用户手机是否有安装app&#xff0c;进行查询包名是否存在时&#xff0c;getPackageManager().getPackageInfo&#xff08;&#xff09;这个函数一直返回null &#xff0c;Android 11增加了权限要求。 1、只是查询指定的App 包 只需要在Andro…...

4、数据结构

数据结构01 数值处理 取整 日常用的四种 / 整数除法&#xff0c;截取整数部分math.Ceil 向上取整 “理解为天花板&#xff0c;向上取值”math.Floor 向下取整 “理解为地板&#xff0c;向下取值”math.Round 四舍五入 / 整数除法&#xff0c;截取整数部分 func main() { f…...

qt5.15.2+vs2019源码调试开发环境搭建

说明 一些qt文件不进行源码调试无法知道其中的原理。提高软件质量&#xff0c;从概念原理及应用角度看待必须知道qt类运行原理。 1.安装 在网上找到qt安装包qt-unified-windows-x64-4.5.1-online.exe&#xff0c;安装qt5.15.2&#xff0c;有选择Qt Debug Information Files …...

【数据结构】单链表之--无头单向非循环链表

前言&#xff1a;前面我们学习了动态顺序表并且模拟了它的实现&#xff0c;今天我们来进一步学习&#xff0c;来学习单链表&#xff01;一起加油各位&#xff0c;后面的路只会越来越难走需要我们一步一个脚印&#xff01; &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x…...

网络中使用最多的图片格式有哪些

互联网中的图片格式五花八门吧&#xff0c;我常常分不清各种格式的使用场景和区别&#xff0c;有些常见的格式和很少见的&#xff0c;在此总结。 常见格式 常见的图片格式&#xff0c;有 JPEG、PNG、GIF、BMP、WebP、SVG、TIFF、ICO等&#xff0c; 少见的比如&#xff1a;HD…...

个人常用Linux命令

来自 linux命令学习-2023-8-1 153913.md等 1、切换目录 cd //切换目录 cd change directory cd 目录名 cd .. 返回上一级目录 pwd显示当前所处目录cd 绝对路径 cd ~ 表示一个用户的home目录 cd - 表示上一次访问的目录 cd / 表示进入根目录下//新建目录/data,并且进入/data…...

数据结构——常见简答题汇总

目录 1、绪论 2、线性表 3、栈、队列和数组 4、串 5、树与二叉树 6、图 7、查找 8、排序 1、绪论 什么是数据结构&#xff1f; 数据结构是相互之间存在一种或多种特定关系的数据元素的集合。数据结构包括三个方面&#xff1a;逻辑结构、存储结构、数据的运算。 逻辑结…...

josef约瑟低电压继电器 DY-110 10-109V 辅助电源·DC110V 嵌入式面板安装

DY-110/110V电压继电器 系列型号 DY-110电压继电器&#xff1b;GY-110电压继电器&#xff1b; GDY-110电压继电器&#xff1b;DY-110/AC电压继电器&#xff1b; GY-110/AC电压继电器&#xff1b;GDY-110/AC电压继电器&#xff1b; DL-110电压继电器&#xff1b;GL-110电压…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

【2025年】解决Burpsuite抓不到https包的问题

环境&#xff1a;windows11 burpsuite:2025.5 在抓取https网站时&#xff0c;burpsuite抓取不到https数据包&#xff0c;只显示&#xff1a; 解决该问题只需如下三个步骤&#xff1a; 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法&#xff1a;原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件&#xff0c;如包含恶意代码、敏感数据或欺诈内容的文档&#xff0c;在企业协同办公环境中&#xff08;如Teams、Google Workspace&#xff09;尤为重要。结合大模型技术&…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

虚拟电厂发展三大趋势:市场化、技术主导、车网互联

市场化&#xff1a;从政策驱动到多元盈利 政策全面赋能 2025年4月&#xff0c;国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》&#xff0c;首次明确虚拟电厂为“独立市场主体”&#xff0c;提出硬性目标&#xff1a;2027年全国调节能力≥2000万千瓦&#xff0…...

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…...