WebGPU 编码与原理(1):绘制三角形

WebGPU 编码与原理(1):绘制三角形

2023年4月7日,谷歌宣布在 Chrome 用户可在 113 Beta 版本中,启用全新的 WebGPU 图形 API,支持硬件图形加速。

本系列是学习记录,尚不能称之为教程(因此可能在代码的实现上、原理的阐述上等都可能存在不合适、不严谨或错误)。该系列希望通过代码撰写以及解读代码的含义,尝试阐述 WebGPU 中相关的图形学效果的实现方法、原理。如有遗漏、错误,还请指正与赐教。

一、绘制三角形

一、gpu

GPU 是 WebGPU 的入口。它具有以下方法和属性:

requestAdapter(options)

从用户代理请求适配器。用户代理选择是否返回适配器,如果是,则根据提供的选项进行选择。

getPreferredCanvasFormat

返回用于显示8位深度的最佳 GPUTextureFormat,该系统上的标准动态范围内容。只能返回 “rgba8unorm” 或 “bgra8unorm” 。

返回值可以作为 format 传递给 GPUCanvasContext 上的 configure()调用,以确保相关的 canvas 能够有效地显示其内容。

注意:没有显示在屏幕上的 canvas 可能会也可能不会从使用这种格式中受益。

wgslLanguageFeatures

Adapters (适配器)可能在任何时候失效(“过期”)。当系统状态发生任何可能影响 requestAdapter() 调用结果的更改时,用户代理应该使以前返回的所有适配器过期。

例如:

  • 添加/移除物理 Adapter(适配器)(通过插入/拔出,驱动程序更新,挂起恢复等)

  • 系统的电源配置已经更改(笔记本电脑被拔下,电源设置更改,等等)

注意:

用户代理可能会经常选择使适配器过期,即使没有系统状态更改(例如,在适配器创建后的几秒或几分钟)。这有助于混淆真实的系统状态变化,并使开发人员更清楚地意识到在调用requestDevice() 之前总是有必要再次调用 requestAdapter() 。如果应用程序遇到这种情况,标准的设备丢失恢复处理应该允许它进行恢复。

const adapter = await navigator.gpu.requestAdapter();

GPU 对象分别通过 Navigator 和 WorkerNavigator 接口在 Window 和 DedicatedWorkerGlobalScope 上下文中可用,并通过 navigator.gpu 公开。

二、GPUAdapter

WebGPU 在计算机的适配器上实现图形计算和渲染工作。适配器包括物理显示适配器(GPU)或软件渲染器(CPU),每个应用程序可以通过 GPUAdapter 同时使用多个适配器。

GPUAdapter 封装了一个 adapter(适配器),并描述了它的功能(特性和限制)。

要获得一个 GPUAdapter,请使用 requestAdapter()。

WebGPU “适配器”(GPUAdapter)是一个对象,用于标识系统上的特定 WebGPU 实现(例如,集成或离散 GPU 上的硬件加速实现,或软件实现)。 同一页面上的两个不同的 “GPUAdapter” 对象可以指代同一个底层实现,或指两个不同的底层实现(例如集成和离散 GPU)。页面可见的适配器集由用户代理决定。

请添加图片描述

(Adapter:<物理设备>允许您查询重要的设备特定详细信息,例如内存大小和功能支持。)

WebGPU “设备”(GPUDevice)表示与 WebGPU 适配器的逻辑连接。 之所以称为“设备”,是因为它抽象了底层实现(例如视频卡)并封装了单个连接:拥有设备的代码可以充当适配器的唯一用户。作为这种封装的一部分,设备是从它创建的所有 WebGPU 对象(纹理等)的根所有者,只要设备丢失或损坏,就可以(内部)释放这些对象。 单个网页上的多个组件可以各自拥有自己的 WebGPU 设备。所有 WebGPU 的使用都是通过 WebGPU 设备或从它创建的对象完成的。 从这个意义上说,它服务于“WebGLRenderingContext”目的的一个子集; 然而,与 WebGLRenderingContext 不同的是,它不与画布对象相关联,并且大多数命令是通过“子”对象发出的。

请添加图片描述

(Device:<逻辑设备>使您可以访问 API的核心内部功能,例如创建纹理、缓冲区、队列、管道等图形数据结构。这种类型的数据结构在所有现代图形 API 中大部分都是相同的,具有非常他们之间几乎没有什么变化。

Vulkan和DirectX 12通过设备创建内存数据结构来提供对内存的控制。)

获得适配器对象后,通过adapter.requestDevice() 获取 GPUDevice设备对象,从而创建缓存(Buffer)、纹理(Texture)、渲染管线(Pipeline)、着色器模块(Shader Module)等,对适配器中的资源进行操作。

const device = await adapter.requestDevice();

三、GPUCanvasContext 和 GPUCanvasConfiguration

利用 GPUCanvasContext 将 WebGPU 的 context 与画布关联,配置 context 的 device、format 等,绘制结果显示在画布 canvas 上。不同于 ebGL,WebGPU 可以将绘制结果直接输出。

  const context = canvas.getContext("webgpu");
  if (!context) return;
  const devicePixelRatio = window.devicePixelRatio || 1;
  canvas.width = canvas.clientWidth * devicePixelRatio;
  canvas.height = canvas.clientHeight * devicePixelRatio;
  const presentationFormat = navigator.gpu.getPreferredCanvasFormat();

  context.configure({
    
    
    device,
    format: presentationFormat,
    alphaMode: "premultiplied"
  });

四、createRenderPipeline

渲染管线是通过设备对象的 createRenderPipeline 方法来创建的。

创建渲染管线需要一个 GPURenderPipelineDescriptor 类型的对象作为参数。

在WebGPU中,渲染管线GPURenderPipeline 由 GPUDevice 创建,在 GPURenderPass 上进行设定。渲染管线接受一个 GPURenderPipelineDescriptor 类型 的参数,包括顶点着色器 vertex 和片元着色器 fragment。

  const pipeline = device.createRenderPipeline({
    
    
    layout: "auto",
    vertex: {
    
    
      module: device.createShaderModule({
    
    
        code: triangleVertWGSL
      }),
      entryPoint: "main"
    },
    fragment: {
    
    
      module: device.createShaderModule({
    
    
        code: redFragWGSL
      }),
      entryPoint: "main",
      targets: [
        {
    
    
          format: presentationFormat
        }
      ]
    },
    primitive: {
    
    
      topology: "triangle-list"
    }
  });

五、createCommandEncoder

在GPU中创建一个指令编码器GPUCommandEncoder,将指令写入GPU的指令缓冲区,执行指令、操控GPU显存或开发调试,最后调用 commandEncoder.finish()命令结束编码器。然后使用 commandEncoder.beginRenderPass() 函数开启一个渲染通道,这个函数接受一个GPURenderPassDescriptor 类型的参数作为渲染通道选项,其中序列 colorAttachments 用于储存图像信息。

  function frame() {
    
    
    const commandEncoder = device.createCommandEncoder();
    if(!context) return
    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);
    passEncoder.setPipeline(pipeline);
    passEncoder.draw(3, 1, 0, 0);
    passEncoder.end();

    device.queue.submit([commandEncoder.finish()]);
    requestAnimationFrame(frame);
  }

六、WGSL Shader

red.frag.wgsl:

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

上面的代码中的:@fragment 表示一种注释,表明接下来的代码应用于 片元阶段

triangle.vert.wgsl:

@vertex
fn main(
  @builtin(vertex_index) VertexIndex : u32
) -> @builtin(position) vec4<f32> {
  var pos = array<vec2<f32>, 3>(
    vec2(0.0, 0.5),
    vec2(-0.5, -0.5),
    vec2(0.5, -0.5)
  );

  return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
}

上面的代码中的:@vertex 表示一种注释,表明接下来的代码应用于 顶点阶段

在 WGSL 中的关键字大致可以分为以下 3 种:

  1. 表明某种类型的关键字,例如 f16、mat2x3、vec2、vec3 等
  2. 一些语法对应的关键字,例如 let、if、for、flase、return 等
  3. 一些保留的关键字,例如 Buffer、Self、as、new、void、yield 等

WGSL 中是不存在隐式转换的

例如数字 1 表示为一个整数,而数字 1.0 则表示为一个浮点数,因此 1 和 1.0 是不相同的两种数值。

七、绘制矩形

基于以上三角形的 demo,修改 WGSL 的 Vertex,来绘制五角形。

三角形的 Vertex 顶点分别为:[0,0.5],[-0.5,-0.5],[0.5,-0.5]

请添加图片描述

同理,矩形的顶点可以为:

请添加图片描述

实现:

1、修改 vert.wgsl 中的顶点,矩形虽然是四个顶点,但绘制是需要五个顶点,第一个和最后一个相同,实现闭合。

请添加图片描述

2、绘制方式修改为 triangle-strip

请添加图片描述

3、draw 方法第一个参数修改为 5

请添加图片描述

效果:

请添加图片描述

八、绘制带孔洞的多边形(矩形)

1、带孔洞的矩形 需要明晰外多边形坐标与内多边形坐标,这里多边形都用矩形。

请添加图片描述

2、知道坐标后,需要陈列出多边形顶点:需要明晰绘制关系、闭合关系,比如这里虽然带孔洞的矩形可以分别用两个矩形,共8个顶点表示:[0.0,0,3],[0.0,0.0],[0.3,0.0],[0.3,0.3] 和 [0.1,0.2],[0.1,0.1],[0.2,0.1],[0.2,0.2]。

但是由于 WebGPU 的绘制方式 triangle-strip,所以这里的顶点使用了 19 个:

请添加图片描述

请添加图片描述

3、绘制结果如下:

请添加图片描述

4、绘制流程大致和下面视频的演示一致:

请添加图片描述

这里是使用部分梯形和三角形的方式,实现的带孔洞的绘制。其思想和 earcut 这种多边形三角剖分库多少有点相似之处。

代码仓库地址:TianWebGPU

参考链接:

WebGPU 接口

Web API WebGPU 接口参考

WebGPU 学习

WebGPU 规范篇 08 管线

webgpu.rocks

WGSL-attribute-location

Webgl中的基础模型绘制

猜你喜欢

转载自blog.csdn.net/yinweimumu/article/details/130044688