顶点着色器
按照语法要求,WGSL着色器的代码,要以字符串的形式存在。若使用ES6的语法模板字符串``
(反引号),实现字符串的多行书写很方便。
@vertex,说明此处是WGSL语言;
fn 关键字声明一个函数,命名为main,作为顶点着色器代码的入口函数。fn 关键字类似JavaScript语言的 function 关键字,用来声明一个函数;
@location( ),用来指定顶点缓冲区相关的顶点数据 、@location(0)表示GPU显存中标记为0的顶点缓冲区中顶点数据。
执行@location(0) pos
给main函数参数@location(0)
表示的顶点数据设置一个变量名pos。
执行@location(0) pos: vec3<f32>
给main函数参数pos
设置数据类型;vec3
表示pos变量的数据类型是三维向量,<f32>
表示三维向量x、y、z几个属性的值都是32位浮点数。
在WGSL中,数据类型除了三维向量vec3,还有四维向量vec4,从三维是可以转换到四维向量的:
渲染管线是一条流水线,顶点着色器处理好的顶点数据,最后需要通过关键字return
返回,这样渲染管线的下个环节,就可以使用了
main函数return
返回的变量,需要通过->
符号设置函数返回值的数类类型,-> vec4<f32>
表示函数返回的变量是浮点数构成的四维向量vec4
。
main函数的返回是顶点数据,这时候除了设置返回值数据类型,还需要设置@builtin(position)
,表明返回值是顶点位置数据。
将顶点着色器代码转换为GPU着色器代码块
通过GPU设备对象的 .createShaderModule() 方法
代码语言:txt复制// 引入顶点着色器vertex代码对应字符串
import { vertex } from './shader.js'
// 字符串形式的顶点着色器代码作为code属性的值
device.createShaderModule({ code: vertex })
代码语言:txt复制把顶点着色器代码块对象
device.createShaderModule({ code: vertex })
作为渲染管线参数vertex.module
属性的值,这样就可以配置好渲染管线上顶点着色器功能单元,要执行的顶点着色器代码。
const pipeline = device.createRenderPipeline({
vertex: {
// 设置渲染管线要执行的顶点着色器代码
module: device.createShaderModule({ code: vertex }),
entryPoint: "main"
},
});
- entryPoint 属性指定着色器代码的入口函数;
图元装配: primitive.topology
代码语言:txt复制const pipeline = device.createRenderPipeline({
primitive: {
topology: "triangle-list",// 绘制三角形
//topology: "line-strip",// 多个定点依次连线
//topology: "point-list",// 每个顶点坐标对应位置渲染一个小点
}
});
光栅化、片元着色器
代码语言:txt复制经过光栅化处理得到的片元,你可以认为是一个没有任何颜色的片元(像素),需要通过渲染管线上片元着色器上色,片元着色器单元就像流水线上一个喷漆的工位一样,给物体设置外观颜色。片元着色器同顶点着色器一样,只能识别WGSL语言
@fragment
fn main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}
- @location(0),输出的片元像素数据存储到显卡内存上,并把位置标注为0,用于渲染管线的后续操作和处理。
- 片元着色器中的 @location(0) 和顶点缓冲区中顶点数据也没关系。
import { vertex, fragment } from './shader.js'
const pipeline = device.createRenderPipeline({
layout: 'auto',
vertex: {
module: device.createShaderModule({ code: vertex }),
entryPoint: "main"//指定入口函数
},
fragment: {
// module:设置渲染管线要执行的片元着色器代码
module: device.createShaderModule({ code: fragment }),
entryPoint: "main",//指定入口函数,
targets: [{
format: format//和WebGPU上下文配置的颜色格式保持一致
}]
}
});
创建命令编码器commandEncoder和渲染通道renderPass
- 首先通过GPU设备对象的方法
.createCommandEncoder()
创建一个命令编码器对象。
const commandEncoder = device.createCommandEncoder();
2. 通过命令对象的方法.beginRenderPass()
可以创建一个渲染通道对象 renderPass
const renderPass = commandEncoder.beginRenderPass({
// 需要配置一些参数
});
3. 通过GPU命令编码器对象commandEncoder
可以控制渲染管线pipeline
渲染输出像素数据。
颜色缓冲区的概念
类比顶点缓冲区和理解颜色缓冲区,顶点缓冲区的功能是存储顶点数据,颜色缓冲区的功能是存储渲染管线输出的像素数据。颜色缓冲区和顶点缓冲区类似,可以创建,不过有一个比较特殊,就是canvas画布对应一个默认的颜色缓冲区,可以直接使用。如果你希望webgpu绘制的图形,呈现在canvas画布上,就要把绘制的结果输出到canvas画布对应的颜色缓冲区中
.beginRenderPass()
创建的 渲染通道 对象 renderPass
具有多个属性,比如:colorAttachments(颜色附件)
渲染通道 renderpass 可以控制渲染管线 pipeline 的渲染像素数据,输出的像素数据会储存到颜色缓冲区;
colorAttachments
属性就和颜色缓冲区有关,colorAttachments
属性的值是数组,数组里面的元素是对象,可以包含多个对象,每个对象的都和一个颜色缓冲区相关,每个对象具有view
、loadOp
、storeOp
、clearValue
等属性。当需要把渲染管线的像素数据要储存到多个缓冲区时,数组中才需要设置多个对象;
const renderPass = commandEncoder.beginRenderPass({
// 给渲染通道指定颜色缓冲区,配置指定的缓冲区
colorAttachments:[{
// 指向用于Canvas画布的纹理视图对象(Canvas对应的颜色缓冲区)
// 该渲染通道renderPass输出的像素数据会存储到Canvas画布对应的颜色缓冲区(纹理视图对象)
view: context.getCurrentTexture().createView(),
storeOp: 'store',//像素数据写入颜色缓冲区
loadOp: 'clear',
clearValue: { r: 0.5, g: 0.5, b: 0.5, a: 1.0 }, //背景颜色
}]
});
顶点缓冲区数据0与渲染管线shaderLocation相关联
代码语言:javascript复制renderPass.setPipeline(pipeline);
- 实际开发,可以通过
device.createBuffer()
创建多个顶点缓冲区,第一个案例,只有一个顶点缓冲区 .setVertexBuffer() 的参数1设置为0即可
绘制命令
渲染通道对象renderPass
提供了一个方法.draw()
// 绘制命令.draw()绘制顶点数据
renderPass.draw(3);
在调用 .draw() 方法之前,要先设置渲染管线
渲染通道结束命令
代码语言:javascript复制渲染通道对象
renderPass
的.end()
方法比较简单,就是字面意思结束,不用设置参数。一般你设置好绘制等命令后,需要设置renderPass.end()
// 渲染通道结束命令.end()
renderPass.end();