WebGPU学习(1)---在WebGPU上绘制三角形

news2024/9/24 7:23:25

在本文中,我们将使用 WebGPU 绘制一个简单的三角形。示例地址

初始化 WebGPU

WebGPU 初始化的流程比 WebGL 要更复杂。

在 WebGL 中,我们只需从 Canvas 元素获取 WebGL 渲染上下文,如 getContext(“webgl” 或者 “webgl2”)。

const gl = canvas.getContext("webgl2");

对于 WebGPU 来说,不仅需要获取上下文,还需要获取设备。

// 获取 webgpu 上下文
const context = canvas.getContext('webgpu') as GPUCanvasContext;

// 获取device
const g_adapter = await navigator.gpu.requestAdapter();
const g_device = await g_adapter.requestDevice();

关于adapter和device的区别,adapter是指物理设备(物理GPU),device是指逻辑设备(抽象GPU)。

一般的低级API比如Vulkan可以从物理设备上获取很多供应商特定的信息比如GPU供应商信息,但是目前的WebGPU并不能获取很多特定的信息。

接下来,我们来设置上下文。

const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
context.configure({
    device: g_device,
    format: presentationFormat,
    alphaMode: 'opaque', // or 'premultiplied'
});

context.configure函数中我们需要指定deviceformatalphaMode

对于device,指定之前获取的逻辑设备g_device

对于format,可以通过navigator.gpu.getPreferredCanvasFormat()获取 canvas 的原生像素格式。该函数通常返回“rgba8unorm”或“bgra8unorm”的格式。

alphaMode指定字符串 ‘opaque’。alphaMode设置的是 Canvas 和 HTML 元素背景的混合方式。如果设置为’opaque’,则用 WebGPU 绘图内容完全覆盖。也可以为alphaMode 设置为 ‘premultiplied’ (相当于alpha预乘),在这种情况下,作为 WebGPU 绘图的结果,如果画布像素的 alpha 小于 1,则该像素将是画布和 HTML 元素背景混合的颜色。

渲染管线设置

接下来,我们来配置 RenderPipeline。

在 WebGL 中,GPU 的设置是使用各种 gl 函数来设置的,而当前的低级 API(例如 WebGPU)是将 GPU 的大部分设置捆绑在一个通常称为 PipelineStateObject (PSO) 的对象中。

PSO 是对 GPU 的每个处理步骤(流水线步骤)的设置的抽象。通过预先创建多个这样的设置并根据情况将它们附加到 GPU,可以很快速的更改 GPU 的流水线状态。

在 WebGPU 中,这个 PSO 被称为 RenderPipeline。

初始化WebGPU之后,接下来我们就进入这个RenderPipeline的设置。RenderPipeline 有 5 个主要类别可以设置:

  • GPUVertexState
  • GPUFragmentState
  • GPUPrimitiveState
  • GPUDepthStencilState
  • GPUMultisampleState

其中,前三个设置基本是必不可少的,让我们先看一下设置这三个状态的代码。

  // create a render pipeline
  const pipeline = g_device.createRenderPipeline({
    layout: 'auto',
    vertex: {
      module: g_device.createShaderModule({
        code: vertWGSL,
      }),
      entryPoint: 'main',
    },
    fragment: {
      module: g_device.createShaderModule({
        code: fragWGSL,
      }),
      entryPoint: 'main',
      targets: [
        // 0
        { // @location(0) in fragment shader
          format: presentationFormat,
        },
      ],
    },
    primitive: {
      topology: 'triangle-list',
    },
  });

在大多数情况下,pipeline的layout设置是’auto’。

接下来我们来看 vertex 的设置。我们将着色器字符串传递给逻辑设备g_devicecreateShaderModule函数。此外,entryPoint指定的是将成为着色器入口函数的名称。

接下来是 fragment 的设置。moduleentryPoint的设置与 vertex 类似,但是还有一个属性叫做 targets,它指定了要绘制到的渲染目标的格式。我们在这里指定用navigator.gpu.getPreferredCanvasFormat()获取的格式,和设置 context 时一样。

最后是 primitive 的设置。这是一个字符串,指定要绘制的几何图形的拓扑类型。

在绘制功能中做的事情

初始化 WebGPU 并创建 RenderPipeline 后,就可以编写渲染函数了。

CommandEncoder(命令编码器)

WebGPU 等低级 3D API 通常有一个称为 CommandBuffer 的缓冲区,用于将各种指令打包到 GPU。在 WebGPU 中,它被称为 CommandEncoder。

const commandEncoder = g_device.createCommandEncoder();

CommandEncoder生成后我们来调用函数beginRenderPass,得到 renderPassEncoder 对象。这个 renderPassEncoder 会加载各种指令,但首先需要提前准备一个GPURenderPassDescriptor类型的参数对象,用于调用beginRenderPass函数。

  const commandEncoder = g_device.createCommandEncoder();
  const textureView = context.getCurrentTexture().createView();
  const renderPassDescriptor: GPURenderPassDescriptor = {
    colorAttachments: [
      {
        view: textureView,
        clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },
        loadOp: 'clear',
        storeOp: 'store',
      },
    ],
  };
  const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);

WebGPU 有一个render pass(一系列绘图处理单元)的概念,为了加载命令,需要先设置这个 render pass。

在渲染过程中设置colorAttachments,这是要绘制到的颜色缓冲区的设置。

对于view,我们要指定要绘制到的渲染目标纹理。对于基本使用来说,我们可以指定通过调用 getCurrentTexture().createView()方法从上下文中获得对象。这也意味着是与我们正在绘制的画布相关联的缓冲区。

对于loadOp,是指定在执行各种指令之前要执行的处理。如果指定’clear’,代表首先要清除指定的缓冲区。在clearValue中就要设置清除的颜色信息。

storeOp指定执行各种命令后如何处理缓冲区。这次我们将保持原样,因此指定’store’。

加载命令

接下来,我们将添加命令。不过,由于我们这次只画三角形,所以命令的数量并没有那么多。

首先,使用setPipeline函数设置上面创建的 RenderPipeline。然后调用draw函数(这里其实就是一个实际的绘制命令)。最后,调用end函数来表示记录命令序列结束。

然后,把 commandEncoder 的finish()函数的返回值交给g_device.queue.submit()。这里的意思就是向 GPU 发出命令,GPU 接到命令将绘制三角形。

  const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
  passEncoder.setPipeline(pipeline);

  // 绘制图元
  // param vertexCount - 要绘制的顶点数.
  // param instanceCount - 要绘制的实例数.
  // param firstVertex - 顶点缓冲区中开始绘制的偏移量(以顶点为单位).
  // param firstInstance - 第一次绘制的实例.
  passEncoder.draw(3, 1, 0, 0);
  // 完成记录渲染过程命令序列
  passEncoder.end();

  g_device.queue.submit([commandEncoder.finish()]);

哪些可以提前设置,哪些不能

与 WebGL 相比,WebGPU 的优势在于可以保存和重复使用 GPU 设置,该配置对象就是 RenderPipeline。WebGL 就做不到这一点,每次切换绘图对象时都需要再次调用各种 gl 函数,但是我们使用 WebGPU,我们就可以切换到预先设置的 RenderPipeline。

另一方面,有些东西是不能预先创建的,也就是必须在每次绘制时生成和设置的东西,即本例中的 CommandEncoder 和 RenderPassEncoder。某些 API(例如 Vulkan)可以创建和重用等效项,但遗憾的是 WebGPU 无法做到这一点。

尽管如此,WebGPU 重用 RenderPipelines 的能力就使其成为比 WebGL 更高效的 API。

WebGPU 着色器语言 WGSL

WebGPU 的着色器语言是 WGSL,一种具有类似于 Rust 语言的独特语法的语言。

在这个示例中,每个顶点着色器和片元着色器的着色器代码如下:

// 顶点着色器
@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>(0.5, 0.0, 0.0, 0.5);
}

顶点着色器

第一个@vertex意味着这是一个顶点着色器。接下来fn是 function 缩写, Rust 语言也是用 fn 来写函数声明,所以 wgsl 很像 Rust。

@builtin(vertex_index) VertexIndex : u32 

@builtin 意味着使用已经内置到着色器语言 WGSL 中的变量。换句话说,它是一个内部变量,可以作为标准使用,而无需在 WebGPU 端进行任何特殊设置。这里声明vertex_index为内部变量,然后起了一个VertexIndex的别名。: u32是 VertexIndex 的类型,代表无符号 32 位整型。

) -> @builtin(position) vec4<f32> {

->代表主函数的返回类型,我们可以看到类型是@builtin(position) vec4<f32>@bultin(position)意味着是position也是一个 WGSL 内部变量,这相当于 GLSL 中的gl_Positionvec4<f32>position的类型,这代表着它是一个4向量类型的 32 位浮点数。

片段着色器

第一个@fragment意味着这是一个片段着色器。

fn main() -> @location(0) vec4<f32> {
  return vec4<f32>(0.5, 0.0, 0.0, 0.5);
}

-> @location(0) vec4<f32>显示了主函数的返回值,这是片元着色器的输出。其中@location(0)对应于fragment.targets数组中的第0个格式,也对应于renderPassDescriptor.colorAttachments数组中的第0个规范。@location(0)也意味着片元着色器的context.getCurrentTexture().createView();指向画布的当前缓冲区。

结论

WebGPU 比 WebGL 需要更多的初始化和规范,所以一开始可能会很困难,但是一旦掌握了它,高性能的绘图体验就在等待着你。此外,由于设计理念比 WebGL 更符合当前 GPU 的设计,因此它有助于理解现代 GPU 的结构。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/355239.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浙江工业大学关于2023年MBA考试初试成绩查询及复试阶段说明

根据往年的情况&#xff0c;2023浙江工业大学MBA考试初试成绩可能将于2月21日公布&#xff0c;为了广大考生可以及时查询到自己的分数&#xff0c;杭州达立易考教育为大家汇总了信息。 1、初试成绩查询&#xff1a;考生可登录中国研究生招生信息网“全国硕士研究生招生考…

Redis学习【9】之Redis RDB持久化

文章目录一 AOF(Append Only File) 持久化二 AOF 基础配置2.1 AOF的开启2.2 文件名配置2.3 混合式持久化开启2.4 AOF 文件目录配置三 AOF 文件格式3.1 Redis 协议3.2 查看 AOF 文件3.3 清单文件3.4 Rewrite 机制3.4.1 rewrite简介3.4.2 rewrite 计算策略3.4.3 手动开启 rewrite…

极客时间左耳听风-高效学习

左耳听风——高效学习篇 P95 | 高效学习&#xff1a;端正学习态度 本人真实⬇️⬇️⬇️⬇️ “ 大部分人都认为自己爱学习&#xff0c;但是&#xff1a; 他们都是只有意识没有行动&#xff0c;他们是动力不足的人。 他们都不知道自己该学什么&#xff0c;他们缺乏方向和目标。…

基于RK3588的嵌入式linux系统开发(四)——uboot镜像下载(基于RKDevTool工具)

官方提供的SDK中包含RKDevTool工具&#xff08;RKDevTool_Release_v2.92&#xff09;和相应的驱动&#xff08;DriverAssitant_v5.1.1&#xff09;。本节主要介绍在windows操作系统环境下利用RKDevTool下载以上生成的uboot镜像和bootloader镜像。注意&#xff1a;本节使用的板卡…

什么是Type-c口?Type-c口有什么优势?

什么是Type-C接口 Type-C接口有哪些好处坏处 说起“Type-C”&#xff0c;相信大家都不会陌生&#xff0c;因为最近拿它大做文章的厂商着实不少&#xff0c;但要具体说清楚Type-C是什么&#xff0c;估计不少人只能说出“可以正反插”“USB的一种”之类的大概。其实&#xff0c;T…

JavaEE|网络编程基础与Socket套接字

文章目录一、为什么需要网络编程二、什么是网络编程三、网络编程中的基本概念1.发送端和接收端2.请求和响应3.客户端和服务端4.常见的客户端服务端模型四、Socket套接字概念及分类1.概念2.分类1&#xff09;流套接字&#xff1a;使用传输层TCP协议2&#xff09;数据报套接字&am…

LeetCode 430. 扁平化多级双向链表

原题链接 难度&#xff1a;middle\color{orange}{middle}middle 题目描述 你会得到一个双链表&#xff0c;其中包含的节点有一个下一个指针、一个前一个指针和一个额外的 子指针 。这个子指针可能指向一个单独的双向链表&#xff0c;也包含这些特殊的节点。这些子列表可以有一…

2023年前端面试知识点总结(JavaScript篇)

近期整理了一下高频的前端面试题&#xff0c;分享给大家一起来学习。如有问题&#xff0c;欢迎指正&#xff01; 1. JavaScript有哪些数据类型 总共有8种数据类型&#xff0c;分别是Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt Null 代表的含义是空对象…

蓝图通讯之事件分发器用法

在事件分发器点 加号添加一个分发器 image.png在蓝图开始运行就进行绑定这个事件, image.png他会创建一个以分发器开头_事件的东西. 而绑定类似,只是没有创建和连接 image.pngimage.png然后 我们设置一个键触发这个逻辑. image.png最后验证多次输入多次触发,类似编程中的事件订…

【博客626】不同类型的ARP报文作用以及ARP老化机制

不同类型的ARP报文作用以及ARP老化机制 1、ARP协议及报文 2、不同类型的ARP报文作用 3、ARP工作原理 4、ARP老化机制 5、Linux ARP老化机制 ARP状态机&#xff1a; 在上图中&#xff0c;我们看到只有arp缓存项的reachable状态对于外发包是可用的&#xff0c;对于stale状态的…

excel应用技巧:F功能键诸多应用汇总

F1&#xff5e;F12&#xff0c;个个都是Excel操作的好帮手。单单一个F4键就有多种用法&#xff0c;其中第4种等间距复制图形&#xff0c;真的很神奇呀。利用快捷键可以代替鼠标做一些工作&#xff0c;也可以实实在在提高我们日常的工作效率&#xff0c;今天就先来跟大家一起分享…

数据结构与算法—队列

队列 队列介绍 有序列表&#xff0c;可以用数组或者链表实现。遵循先进先出原则。 数组实现队列 public class ArrayQueue {public static void main(String[] args) {ArrayQueue queue new ArrayQueue(3);// 接收用户输入char key ;Scanner sc new Scanner(System.in);…

PyQt5 自定义富文本编辑器

介绍 一款使用PyQt5和网页端框架wangEditor集成的富文本编辑器 代码片段 PyQt5客户端 与网页端建立连接def create_connect(self):self.web_view QWebEngineView()self.bridge JSBridge(self.web_view.page())self.web_view.load(QUrl.fromLocalFile(self.editor_path))w…

现代卷积神经网络经典架构图

卷积神经网络&#xff08;LeNet&#xff09; LeNet 的简化版深层卷积神经网络&#xff08;AlexNet&#xff09; 从LeNet&#xff08;左&#xff09;到AlexNet&#xff08;右&#xff09;改进&#xff1a; dropOut层 - 不改变期望但是改变方差ReLU层 - 减缓梯度消失MaxPooling数…

2.18 设置language和中文输入法

文章目录一&#xff1a;设置language二&#xff1a;设置中文输入法一&#xff1a;设置language nvidia的开发板上默认只有English&#xff0c;需要点击如下管理&#xff1a; 接着进入如下界面&#xff1a; 此时图中的“汉语&#xff08;中国&#xff09;”应该是没有的&…

Kubernetes是个什么东东?

Kubernetes 是一个可移植、可扩展的开源平台&#xff0c;用于管理容器化的工作负载和服务&#xff0c;可促进声明式配置和自动化。 Kubernetes 拥有一个庞大且快速增长的生态&#xff0c;其服务、支持和工具的使用范围相当广泛。 Kubernetes 这个名字源于希腊语&#xff0c;意…

NoMachine 输入用户名密码后 闪断 解决办法

大家好&#xff0c;我是虎哥&#xff0c;最近工作忙&#xff0c;好长时间没有继续套件的深度学习&#xff0c;今天周六&#xff0c;难得有空&#xff0c;泡好茶&#xff0c;打开电脑&#xff0c;链接套件桌面&#xff0c;得&#xff0c;出问题了&#xff0c;一个很奇怪的问题&a…

[教你传话,表白,写信]

第一步 关注飞鸽传话助手 第二部 点击链接进入 第三步 点击发送,输入内容 第四步 就可以收到了

Simulink 自动代码生成电机控制:STM32 Encoder编码器使用总结

目录 Encoder 原理 STM32 Encoder 计数原理 模型仿真 模拟Encoder 基于Encoder计算角度和速度 关于启动的仿真 代码生成 运行演示 总结 总结一下基于STM32的Encoder接口的电机运行&#xff0c;相应的仿真和实验都是基于一个1024脉冲的增量式光电编码器&#xff0c;关于…

23年校招DL/NLP/推荐系统/ML/算法基础面试必看300问及答案

2020年校招已经开始了&#xff0c;在疫情全球肆虐的背景下&#xff0c;全球就业情况异常艰难&#xff0c;加上美国对中国企业打压持续升级&#xff0c;对于马上开始秋招找工作的毕业生而言&#xff0c;更是难上加难。我们不能凭一己之力改变现状&#xff0c;但我们可以凭借自己…