一、WebGPU与Canvas画布
想要使用浏览器获取到GPU设备对象device需要进行以下两个步骤:
- 通过
.requestAdapter方法获取GPU适配器 - 通过
.requestDevice方法获取GPU设备对象
注意:这两个方法都是异步函数,需要使用promise或者await的方法进行获取
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
使用WebGPU进行图像渲染需要使用canvas作为画布,该标签通常用于可视化领域
WebGPU要想在canvas上进行绘制需要通过.getContext进行上下文联系
const canvas = document.getElementById('webgpu');
const context = canvas.getContext('webgpu');
另外这个上下文还需要进行相关的配置,分别需要传入device(设备)和format(颜色格式)
context.configure({
device, // 传入GPU设备对象
format // 传入颜色格式
})
其中format如果没有特殊需求可以直接通过gpu的.getPreferredCanvasFormat方法获取
以下为完整的画布配置
if (!navigator.gpu) {
console.log('No');
}
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
// 配置canvas上下文
const canvas = document.getElementById('webgpu');
const context = canvas.getContext('webgpu');
const format = navigator.gpu.getPreferredCanvasFormat();
context.configure({
device, format
})
二、顶点Vertex与渲染管线Pipeline
使用WebGPU渲染一个物体时需要用到顶点Vertex的概念,通过顶点来定义物体的几何形状
例如渲染一个三角形时需要分别定义三角形三个角对应顶点的坐标
2.1WebGPU坐标系
在使用canvas渲染WebGPU图形时,坐标原点通常是这个canvas画布的几何中心,并以该原点为中心垂直向上构建坐标系的y轴,水平向右构建坐标系的x轴,垂直向屏幕内构建坐标系的z轴
在WebGPU中,所有的坐标均采用的是相对值,也就是说x轴的最左边坐标无论何时均为(-1, 0),最右边为(1, 0),同样的y轴最上边为(0, 1),最下边为(0, -1)
在开发中由于涉及到的图形较为复杂,导致顶底较多,因此通常会使用类型化数组的方式来定义顶点数据,例如使用32位浮点数数组来表达三角形的坐标:
const vertexArray = new Float32Array([
1.0, -1.0, 0.0, // 表示顶点1
-1.0, -1.0, 0.0, // 表示顶点2
0.0, 1.0, 0.0 // 表示顶点3
])
2.2顶点缓冲区
开辟缓冲区
完成顶点在画布中对应位置的配置后,需要建立相应的顶点缓冲区
顶点缓冲区:包含顶点数据的内存缓冲区,顶点缓冲区可包含可呈现的任何顶点类型,另外可以处理顶点缓冲区中的顶点以执行转换、照明、生成剪裁标志等操作
通过.createBuffer方法创建一个顶点缓冲区
const vertexBuffer = device.createBuffer();
可以理解为通过该命令可以在计算机的显存(GPU内存)中开辟一片存储空间用于存储vertex数据,后续的操作均在该缓冲区内进行
在通过.createBuffer方法创建缓冲区时需要传入一个对象用于配置该缓冲区
const vertexBuffer = device.createBuffer({
size: vertexArray.byteLength,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
});
- size:该属性表示缓冲区的字节长度(即缓冲区开辟多大的空间,本例中是vertexArray中长度9 * 4 = 36)
- usage:该属性用于标注该顶点缓冲区的作用
顶点数据写入缓冲区
device设备对象有一个queue属性具备一个.writeBuffer方法,用于将类型化数组中的数据写入已经开辟好的顶点缓冲区中
该方法需要传入三个参数:
- 参数一:需要传入的顶点缓冲区
- 参数二:从类型化数组获取顶点数据的偏移量(单位为字节),如果为0表示从类型化数组的开头开始读数据
- 参数三:存有顶点数据的类型化数组
const queue = device.queue;
device.queue.writeBuffer(vertexBuffer, 0, vertexArray)
2.3渲染管线
概念
- 渲染管线:可以理解成一种工厂的流水线,流水线上会提供不同的功能单元完成不同的零部件生产
GPU面对大型渲染环境成千上万的三角面,如果逐一进行单个计算会导致损失率大大提升
类比于汽车生产,在使用流水型进行汽车生产之前,汽车组装只能让一位位工人逐工序完成,效率极低,而引入了流水线概念后每位工人只需要做不停地做同一道工序,所有工序并行进行,极大地提高了工厂的生产效率
同样的,GPU采用了数量众多的计算单元和超长的流水线,但每一个部分只有非常简单的控制逻辑
总的来说渲染流程中需要进行三个大步骤对图像进行渲染,分别是应用阶段、几何阶段、光栅化阶段
- 应用阶段:CPU将决定递给GPU什么样的数据,例如渲染目标场景中的灯光、场景的模型、摄像机的位置,有时候还会对这些数据进行处理,并且告诉GPU这些数据的渲染状态
- 几何阶段:从这个阶段开始,进入了流水线。该阶段将把应用阶段发来的数据进行进一步处理,而这个阶段又可以进一步细分为若干个流水线阶段,可以理解为工厂流水线上进行的工序
- 光栅化阶段:对几何阶段处理后的顶点数据进行图元组装,三角形遍历等操作,并最终生成屏幕图像呈现在Canvas画布中
创建渲染管线
使用.createRenderPipline方法构建一个渲染管线,并传入一个对象用于进行相关配置
const pipline = device.createRenderPipeline({
// 配置顶点相关参数
vertex: {
// 顶点所有缓冲区模块配置,该设置是一个对象数组
// 需要对所有需要传入的顶点缓冲区进行配置,每个顶点缓冲区是一个单独的对象
// 本例中只有一个顶点缓冲区,因此只需要传入一个对象
buffers: [
{
arrayStride: 3 * 4,// 一个顶点数据占用的(每间隔多少个数据来读一个顶点,例如一个顶点坐标为(1.0, 0.0, 0.0),下一个顶点为(0.0, 0.0, 0.0)这两个顶点之间隔了3个浮点数,也就是3*浮点数长度4)
// 顶点缓冲区属性
attribute: [{
shaderLocation: 0, // 标记顶点缓冲区存储位置
format: 'float32x3', // 每个顶点的设置(本例中是32位浮点数,xyz三个轴确认点的坐标,因此为float32x3)
offset: 0 // 读数据的偏移量
}]
}
]
}
})