WebGPU学习-开篇(一)

473 阅读2分钟

介绍

最近感觉工作不好找,想着另辟蹊径学点不一样的东西。做一个小小的转行,然后就有了从0开始学习WebGPU这个事情了

为什么要学习WebGPU

  • WebGPU更好地支持多线程
  • WebGPU支持compute shader,从而让程序员能利用GPU实现很多优化
  • WebGPU与WebGL2的区别很大,两者不容易兼容。如果要从WebGL1升级,最好直接升级到WebGPU,一劳永逸
  • WebGPU是标准,各大浏览器都会支持。不像WebGL2,苹果直接不支持。
  • 目前WebGPU应该已经算正式发布,也比较成熟了,也有相关的Demo可供学习
  • 个人对WebGPU将来还是挺看好的,海外的几个大厂都有人在研究。类似苹果电脑这块,将webGPU作为跨平台的载体使用的话,Mac电脑的游戏也能变多并且也已经有hlsl转wgsl的工具,移植起来感觉很方便

一、 初始化WebGPU

1. 判断浏览器是否支持WebGPU

2. 获取GPU的JS逻辑实例

const adapter = await navigator.gpu.requestAdapter({  
    powerPreference: "high-performance"  // 非填,设置WebGPU在高性能下运行
});
const device = await adapter.requestDevice();

3. 配置显示GPU渲染结果的画布

<canvas id="webgpu"></canvas>
#webgpu {
  width: 500px;
  height: 500px;
}
const canvas = document.getElementById("webgpu");
const context = canvas.getContext("webgpu");
const format = navigator.gpu.getPreferredCanvasFormat();
context.configure({
  device,
  format
});

二、 配置GPU管线

1. 创建顶点着色器

WebGPU使用wgsl着色器语言,作为着色器外部资源。详情请看着色器语言WGSL快速了解

@vertex  
fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4<f32> {  
    var pos = array<vec2<f32>, 3>(  
        vec2<f32>(0.0, 0.5),  
        vec2<f32>(-0.5, -0.5),  
        vec2<f32>(0.5, -0.5)  
    );  
    return vec4<f32>(pos[VertexIndex], 0.0, 1.0);  
}
@fragment  
fn main() -> @location(0) vec4<f32> {  
    return vec4<f32>(1.0, 0.0, 0.0, 1.0);  
}
// 顶点向量
import vertex from "../src/assets/triangle.vert.wgsl?raw";
const vertexShader = device.createShaderModule({  
    code: vertex  
});
// 颜色向量
import frag from "../src/assets/red.frag.wgsl?raw";
const fragmentShader = device.createShaderModule({  
    code: frag  
});

2. 创建GPU管线

await device.createRenderPipelineAsync({  
    layout: "auto",    // 必填,不填报错
    vertex: {  
        module: vertexShader,  
        entryPoint: "main"    // wgsl文件的入口函数名
    },  
    fragment: {  
        module: fragmentShader,  
        entryPoint: "main",  
        targets: [{  
            format    // 指定颜色类型
        }]  
    },  
    primitive: {  
        topology: "triangle-list"  
    }  
});

三、 录制 Commadnd 队列

1. 录制命令

const encoder = device.createCommandEncoder();  
const renderPass = encoder.beginRenderPass({  
    colorAttachments: [{  
        view: context.getCurrentTexture().createView(),  
        loadOp: "clear",  
        clearValue: { r: 0.5, g: 0.5, b: 0.5, a: 1 },  
        storeOp: "store"  
   }]  
});

2. 设置管线

renderPass.setPipeline(pipeline);

3. 绘制顶点

renderPass.draw(3);  
renderPass.end();  

4. 提交队列

const buffer = encoder.finish();
device.queue.submit([buffer]);

完整案例