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


    当前示例源码github地址:

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

    系统特性:

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

    2. 基于算力驱动的系统设计。

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

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

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

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

    7. 数据/语义驱动。

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

    9. 保持computing与rendering机制一致性。

             1). 构造一致性。

             2). 使用一致性。

             3). 自动兼容materialmulti-pass、material graph、pass(pass node) graph机制。

    当前示例运行效果:

    WGSL顶点与片段shader:

    1. struct VertexInput {
    2. @location(0) pos: vec3f,
    3. @location(1) uv : vec2f,
    4. @builtin(instance_index) instance: u32
    5. };
    6. struct VertexOutput {
    7. @builtin(position) pos: vec4f,
    8. @location(0) cell: vec2f,
    9. @location(1) uv: vec2f,
    10. @location(2) instance: f32,
    11. };
    12. @group(0) @binding(0) var grid: vec2f;
    13. @group(0) @binding(1) var cellState: array<u32>;
    14. @group(0) @binding(2) var lifeState: array<f32>;
    15. @vertex
    16. fn vertMain(input: VertexInput) -> VertexOutput {
    17. let i = f32(input.instance);
    18. let cell = vec2f(i % grid.x, floor(i / grid.x));
    19. let cellOffset = cell / grid * 2.0;
    20. var state = f32(cellState[input.instance]);
    21. let gridPos = (input.pos.xy * state + 1.0) / grid - 1.0 + cellOffset;
    22. var output: VertexOutput;
    23. output.pos = vec4f(gridPos, 0.0, 1.0);
    24. output.cell = cell;
    25. output.uv = input.uv;
    26. output.instance = i;
    27. return output;
    28. }
    29. @fragment
    30. fn fragMain(input: VertexOutput) -> @location(0) vec4f {
    31. let c = input.cell / grid;
    32. var dis = length(input.uv - vec2<f32>(0.5, 0.5));
    33. dis = min(dis/0.15, 1.0);
    34. let i = u32(input.instance);
    35. var f = clamp((lifeState[i])/100.0, 0.0005, 1.0);
    36. dis = (1.0 - pow(dis, 100.0)) * (1.0 - f) + f;
    37. var c3 = vec3f(c, (1.0 - c.x) * (1.0 - f) + f) * dis;
    38. return vec4f(c3, 1.0);
    39. }

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

    1. const gridSize = 64;
    2. const shdWorkGroupSize = 8;
    3. const compShdCode = `
    4. @group(0) @binding(0) var grid: vec2f;
    5. @group(0) @binding(1) var cellStateIn: array;
    6. @group(0) @binding(2) var cellStateOut: array;
    7. @group(0) @binding(3) var lifeState: array;
    8. fn cellIndex(cell: vec2u) -> u32 {
    9. return (cell.y % u32(grid.y)) * u32(grid.x) +
    10. (cell.x % u32(grid.x));
    11. }
    12. fn cellActive(x: u32, y: u32) -> u32 {
    13. return cellStateIn[cellIndex(vec2(x, y))];
    14. }
    15. @compute @workgroup_size(${shdWorkGroupSize}, ${shdWorkGroupSize})
    16. fn compMain(@builtin(global_invocation_id) cell: vec3u) {
    17. // Determine how many active neighbors this cell has.
    18. let activeNeighbors = cellActive(cell.x+1, cell.y+1) +
    19. cellActive(cell.x+1, cell.y) +
    20. cellActive(cell.x+1, cell.y-1) +
    21. cellActive(cell.x, cell.y-1) +
    22. cellActive(cell.x-1, cell.y-1) +
    23. cellActive(cell.x-1, cell.y) +
    24. cellActive(cell.x-1, cell.y+1) +
    25. cellActive(cell.x, cell.y+1);
    26. let i = cellIndex(cell.xy);
    27. // Conway's game of life rules:
    28. switch activeNeighbors {
    29. case 2: { // Active cells with 2 neighbors stay active.
    30. cellStateOut[i] = cellStateIn[i];
    31. if(cellStateOut[i] > 0) {
    32. lifeState[i] += 0.5;
    33. } else {
    34. lifeState[i] -= 0.5;
    35. }
    36. }
    37. case 3: { // Cells with 3 neighbors become or stay active.
    38. cellStateOut[i] = 1;
    39. lifeState[i] += 0.5;
    40. }
    41. default: { // Cells with < 2 or > 3 neighbors become inactive.
    42. cellStateOut[i] = 0;
    43. lifeState[i] = 0.01;
    44. }
    45. }
    46. if(lifeState[i] < 0.01) { lifeState[i] = 0.01; }
    47. }`;
    48. export class GameOfLifePretty {
    49. private mRscene = new RendererScene();
    50. initialize(): void {
    51. console.log("GameOfLifePretty::initialize() ...");
    52. this.initScene();
    53. }
    54. private createUniformValues(): { ufvs0: WGRUniformValue[]; ufvs1: WGRUniformValue[] }[] {
    55. const gridsSizesArray = new Float32Array([gridSize, gridSize]);
    56. const cellStateArray0 = new Uint32Array(gridSize * gridSize);
    57. for (let i = 0; i < cellStateArray0.length; i++) {
    58. cellStateArray0[i] = Math.random() > 0.8 ? 1 : 0;
    59. }
    60. const cellStateArray1 = new Uint32Array(gridSize * gridSize);
    61. for (let i = 0; i < cellStateArray1.length; i++) {
    62. cellStateArray1[i] = i % 2;
    63. }
    64. const lifeStateArray3 = new Float32Array(gridSize * gridSize);
    65. for (let i = 0; i < lifeStateArray3.length; i++) {
    66. lifeStateArray3[i] = 0.01;
    67. }
    68. let shared = true;
    69. let sharedData0 = { data: cellStateArray0 };
    70. let sharedData1 = { data: cellStateArray1 };
    71. let sharedData3 = { data: lifeStateArray3 };
    72. const v0 = new WGRUniformValue({ data: gridsSizesArray, stride: 2, shared });
    73. v0.toVisibleAll();
    74. // build rendering uniforms
    75. const va1 = new WGRStorageValue({ sharedData: sharedData0, stride: 1, shared }).toVisibleVertComp();
    76. const vb1 = new WGRStorageValue({ sharedData: sharedData1, stride: 1, shared }).toVisibleVertComp();
    77. const vc1 = new WGRStorageValue({ sharedData: sharedData3, stride: 1, shared }).toVisibleAll();
    78. // build computing uniforms
    79. const compva1 = new WGRStorageValue({ sharedData: sharedData0, stride: 1, shared }).toVisibleVertComp();
    80. const compva2 = new WGRStorageValue({ sharedData: sharedData1, stride: 1, shared }).toVisibleComp();
    81. compva2.toBufferForStorage();
    82. const compvb1 = new WGRStorageValue({ sharedData: sharedData1, stride: 1, shared }).toVisibleVertComp();
    83. const compvb2 = new WGRStorageValue({ sharedData: sharedData0, stride: 1, shared }).toVisibleComp();
    84. compvb2.toBufferForStorage();
    85. const compv3 = new WGRStorageValue({ sharedData: sharedData3, stride: 1, shared }).toVisibleComp();
    86. compv3.toBufferForStorage();
    87. return [
    88. { ufvs0: [v0, va1, vc1], ufvs1: [v0, vb1, vc1] },
    89. { ufvs0: [v0, compva1, compva2, compv3], ufvs1: [v0, compvb1, compvb2, compv3] }
    90. ];
    91. }
    92. private mEntity: FixScreenPlaneEntity;
    93. private mStep = 0;
    94. private createMaterial(shaderCodeSrc: WGRShderSrcType, uniformValues: WGRUniformValue[], shadinguuid: string, instanceCount: number): WGMaterial {
    95. return new WGMaterial({
    96. shadinguuid,
    97. shaderCodeSrc,
    98. instanceCount,
    99. uniformValues
    100. });
    101. }
    102. private createCompMaterial(shaderCodeSrc: WGRShderSrcType, uniformValues: WGRUniformValue[], shadinguuid: string, workgroupCount = 2): WGCompMaterial {
    103. return new WGCompMaterial({
    104. shadinguuid,
    105. shaderCodeSrc,
    106. uniformValues
    107. }).setWorkcounts(workgroupCount, workgroupCount);
    108. }
    109. private initScene(): void {
    110. const rc = this.mRscene;
    111. const ufvsObjs = this.createUniformValues();
    112. const instanceCount = gridSize * gridSize;
    113. const workgroupCount = Math.ceil(gridSize / shdWorkGroupSize);
    114. let shaderSrc = {
    115. shaderSrc: {
    116. code: shaderWGSL,
    117. uuid: "shader-gameOfLife",
    118. }
    119. };
    120. let compShaderSrc = {
    121. comp: {
    122. code: compShdCode,
    123. uuid: "shader-computing",
    124. }
    125. };
    126. const materials: WGMaterial[] = [
    127. // build ping-pong rendering process
    128. this.createMaterial(shaderSrc, ufvsObjs[0].ufvs0, "rshd0", instanceCount),
    129. this.createMaterial(shaderSrc, ufvsObjs[0].ufvs1, "rshd1", instanceCount),
    130. // build ping-pong computing process
    131. this.createCompMaterial(compShaderSrc, ufvsObjs[1].ufvs1, "compshd0", workgroupCount),
    132. this.createCompMaterial(compShaderSrc, ufvsObjs[1].ufvs0, "compshd1", workgroupCount),
    133. ];
    134. let entity = new FixScreenPlaneEntity({
    135. x: -0.8, y: -0.8, width: 1.6, height: 1.6,
    136. materials
    137. });
    138. rc.addEntity(entity);
    139. this.mEntity = entity;
    140. }
    141. private mFrameDelay = 3;
    142. run(): void {
    143. let rendering = this.mEntity.isRendering();
    144. if (rendering) {
    145. if (this.mFrameDelay > 0) {
    146. this.mFrameDelay--;
    147. return;
    148. }
    149. this.mFrameDelay = 3;
    150. const ms = this.mEntity.materials;
    151. for (let i = 0; i < ms.length; i++) {
    152. ms[i].visible = (this.mStep % 2 + i) % 2 == 0;
    153. }
    154. this.mStep++;
    155. }
    156. this.mRscene.run(rendering);
    157. }
    158. }

  • 相关阅读:
    golang单线程对比map与bigCache小对象存取性能差别
    VSCode设置中文语言界面(VScode设置其他语言界面)
    RPM软件包管理
    通过yarn提交作业到spark,运行一段时间后报错。
    【Redis GEO】2、地理位置类型的应用场景
    民安智库(第三方社会评估调研公司)华为Mate60Pro携麒麟芯片回归
    龙迅#LT8711UXE1 适用于Type-C/DP1.2/EDP转HDMI2.0方案,支持音频剥离和HDCP功能。
    ChatGPT竞争对手Writer,获得1亿美元融资
    图解 LeetCode 算法汇总——双指针
    【环境搭建】linux docker-compose安装mysql5.7
  • 原文地址:https://blog.csdn.net/vily_lei/article/details/134255999