WebGL

2024-08-23 15:45:18 浏览数 (1)

顶点着色器

顶点着色器代码顶点着色器代码

按照语法要求,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返回,这样渲染管线的下个环节,就可以使用了

顶点着色器的工作结束,return pos2顶点着色器的工作结束,return pos2

main函数return返回的变量,需要通过->符号设置函数返回值的数类类型,-> vec4<f32>表示函数返回的变量是浮点数构成的四维向量vec4

main函数的返回是顶点数据,这时候除了设置返回值数据类型,还需要设置@builtin(position),表明返回值是顶点位置数据。

将顶点着色器代码转换为GPU着色器代码块

通过GPU设备对象的 .createShaderModule() 方法

代码语言:txt复制
// 引入顶点着色器vertex代码对应字符串
import { vertex } from './shader.js'
// 字符串形式的顶点着色器代码作为code属性的值
device.createShaderModule({ code: vertex })

把顶点着色器代码块对象device.createShaderModule({ code: vertex })作为渲染管线参数vertex.module属性的值,这样就可以配置好渲染管线上顶点着色器功能单元,要执行的顶点着色器代码。

代码语言:txt复制
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",// 每个顶点坐标对应位置渲染一个小点
    }
});

光栅化、片元着色器

经过光栅化处理得到的片元,你可以认为是一个没有任何颜色的片元(像素),需要通过渲染管线上片元着色器上色,片元着色器单元就像流水线上一个喷漆的工位一样,给物体设置外观颜色。片元着色器同顶点着色器一样,只能识别WGSL语言

代码语言:txt复制
@fragment
fn main() -> @location(0) vec4<f32> {
    return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}
  1. @location(0),输出的片元像素数据存储到显卡内存上,并把位置标注为0,用于渲染管线的后续操作和处理。
  2. 片元着色器中的 @location(0) 和顶点缓冲区中顶点数据也没关系。
代码语言:javascript复制
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

  1. 首先通过GPU设备对象的方法.createCommandEncoder()创建一个命令编码器对象。
代码语言:javascript复制
const commandEncoder = device.createCommandEncoder();

2. 通过命令对象的方法.beginRenderPass()可以创建一个渲染通道对象 renderPass

代码语言:javascript复制
const renderPass = commandEncoder.beginRenderPass({
    // 需要配置一些参数
});

3. 通过GPU命令编码器对象commandEncoder可以控制渲染管线pipeline渲染输出像素数据。

颜色缓冲区的概念

类比顶点缓冲区和理解颜色缓冲区,顶点缓冲区的功能是存储顶点数据,颜色缓冲区的功能是存储渲染管线输出的像素数据。颜色缓冲区和顶点缓冲区类似,可以创建,不过有一个比较特殊,就是canvas画布对应一个默认的颜色缓冲区,可以直接使用。如果你希望webgpu绘制的图形,呈现在canvas画布上,就要把绘制的结果输出到canvas画布对应的颜色缓冲区中

.beginRenderPass()创建的 渲染通道 对象 renderPass 具有多个属性,比如:colorAttachments(颜色附件) 渲染通道 renderpass 可以控制渲染管线 pipeline 的渲染像素数据,输出的像素数据会储存到颜色缓冲区;

colorAttachments属性就和颜色缓冲区有关,colorAttachments属性的值是数组,数组里面的元素是对象,可以包含多个对象,每个对象的都和一个颜色缓冲区相关,每个对象具有viewloadOpstoreOpclearValue等属性。当需要把渲染管线的像素数据要储存到多个缓冲区时,数组中才需要设置多个对象;

代码语言:javascript复制
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()

代码语言:javascript复制
// 绘制命令.draw()绘制顶点数据
renderPass.draw(3);

在调用 .draw() 方法之前,要先设置渲染管线

渲染通道结束命令

渲染通道对象renderPass.end()方法比较简单,就是字面意思结束,不用设置参数。一般你设置好绘制等命令后,需要设置renderPass.end()

代码语言:javascript复制
// 渲染通道结束命令.end()
renderPass.end();

0 人点赞