日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

WebGPU 入門:繪制一個三角形

來源: 責編: 時間:2023-11-01 09:19:00 332觀看
導(dǎo)讀大家好,我是前端西瓜哥。今天我們來入門 WebGPU,來寫一個圖形版本的 Hello World,即繪制一個三角形。WebGPU 是什么?WebGPU 是一個正在開發(fā)中的潛在 Web 標準和 JavaScript API,目標是提供 “現(xiàn)代化的 3D 圖形和計算能力”

or428資訊網(wǎng)——每日最新資訊28at.com

大家好,我是前端西瓜哥。or428資訊網(wǎng)——每日最新資訊28at.com

今天我們來入門 WebGPU,來寫一個圖形版本的 Hello World,即繪制一個三角形。or428資訊網(wǎng)——每日最新資訊28at.com

WebGPU 是什么?

WebGPU 是一個正在開發(fā)中的潛在 Web 標準和 JavaScript API,目標是提供 “現(xiàn)代化的 3D 圖形和計算能力”。or428資訊網(wǎng)——每日最新資訊28at.com

簡單來說,WebGPU 提供一個更現(xiàn)代的 Web 上的圖形渲染標準。or428資訊網(wǎng)——每日最新資訊28at.com

WebGPU 的出現(xiàn)就是為了取代 WebGL 的,因為后者的 API 實在有些過時,無法利用好現(xiàn)代 GPU 的一些高級特性,本身的 API 設(shè)計也較難使用。or428資訊網(wǎng)——每日最新資訊28at.com

相比 WebGL,WebGPU 有更好的性能表現(xiàn),API 更底層更靈活,并支持更高級的現(xiàn)代特性,比如計算著色器。or428資訊網(wǎng)——每日最新資訊28at.com

毫無疑問,WebGPU 是前端圖形渲染的未來,值得去學(xué)習(xí)一下。or428資訊網(wǎng)——每日最新資訊28at.com

像是以性能著稱的前端圖形庫 PixiJS,也開始進行支持 WebGPU 的工作,并在最近發(fā)布了預(yù)覽版本,聲稱性能將是 WebGL 的 2.5 倍。or428資訊網(wǎng)——每日最新資訊28at.com

or428資訊網(wǎng)——每日最新資訊28at.com

不過目前 WebGPU 還不夠成熟,仍有許多工作要做,且只有少數(shù)瀏覽器的最新版本直接支持或通過設(shè)置開啟。or428資訊網(wǎng)——每日最新資訊28at.com

即使之后所有瀏覽器都支持了,舊版本瀏覽器還是不支持的,離大范圍使用還有相當長的一段路要走。or428資訊網(wǎng)——每日最新資訊28at.com

or428資訊網(wǎng)——每日最新資訊28at.com

只能說未來可期。or428資訊網(wǎng)——每日最新資訊28at.com

但生產(chǎn)中,我們可以做一個回退機制:如果瀏覽器支持 WebGPU,我們用 WebGPU 去渲染,如果不支持就回滾到 WebGL。or428資訊網(wǎng)——每日最新資訊28at.com

只要在底層渲染方案上封裝一層渲染器 renderer,就像 PixiJS 現(xiàn)在做的事情一樣,個人還是比較期待它在性能上的提升的。or428資訊網(wǎng)——每日最新資訊28at.com

繪制三角形

OK,我們開始用 WebGPU 繪制一個三角形。or428資訊網(wǎng)——每日最新資訊28at.com

確保你的瀏覽器支持 WebGPU,建議用 Chrome,并更新到最新版本。or428資訊網(wǎng)——每日最新資訊28at.com

這里我們創(chuàng)建一個寬高各為 300 的 canvas 元素,用于繪制圖形。or428資訊網(wǎng)——每日最新資訊28at.com

<canvas width="300" height="300"></canvas>

初始化 WebGPU 相關(guān)的一些對象。or428資訊網(wǎng)——每日最新資訊28at.com

adapter 和 device

創(chuàng)建一個適配器對象 adapter,適配器是一個 GPU 物理硬件設(shè)備的抽象。or428資訊網(wǎng)——每日最新資訊28at.com

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

requestAdapter() 方法會查看系統(tǒng)上所有可用的 GPU 設(shè)備,并選擇其中合適的適配器。該方法可以傳一些參數(shù),去按條件匹配。比如 { powerPreference: 'low-power' } 表示優(yōu)先使用低能耗的 GPU。or428資訊網(wǎng)——每日最新資訊28at.com

此外,這個方法返回的是一個 Promise,即它是 異步的,需要用 await 的方式去等待異步的結(jié)果。or428資訊網(wǎng)——每日最新資訊28at.com

然后基于 adapter,調(diào)用 requestDevice 方法拿到設(shè)備對象 device。or428資訊網(wǎng)——每日最新資訊28at.com

device 可以理解為 adapter 的一個會話。做個比喻的話 adapter 是一個公司,device 是一個具體干活的人。or428資訊網(wǎng)——每日最新資訊28at.com

const device = await adapter.requestDevice();

requestDevice() 方法也可以傳入配置項,去開啟一些高級特性,或是指定一些硬件限制,比如最大紋理尺寸。or428資訊網(wǎng)——每日最新資訊28at.com

配置 canvas

類似 canvas 2d 和 webgl,我們需要通過 canvas 元素拿到上下文。or428資訊網(wǎng)——每日最新資訊28at.com

const canvas = document.querySelector('canvas');const ctx = canvas.getContext('webgpu');

接著是調(diào)用 ctx.configure() 方法配置剛剛聲明的 device 對象和像素格式。or428資訊網(wǎng)——每日最新資訊28at.com

const canvasFormat = navigator.gpu.getPreferredCanvasFormat();// 給上下文配置 device 對象和ctx.configure({  device,  format: canvasFormat,});

navigator.gpu.getPreferredCanvasFormat() 會返回當前環(huán)境合適的像素格式的字符串標識,通常是 'bgra8unorm',表示用 8 位無符號整數(shù)來表示藍色、綠色、紅色和透明度四個分量。or428資訊網(wǎng)——每日最新資訊28at.com

設(shè)置背景色

創(chuàng)建命令編碼器 GPUCommandEncoder 實例,它用于編碼需要提交給 GPU 的命令。or428資訊網(wǎng)——每日最新資訊28at.com

const encoder = device.createCommandEncoder();

開啟一個新的渲染通道(Render Pass),這里清空顏色緩沖區(qū)時填充了一個淺藍色背景。or428資訊網(wǎng)——每日最新資訊28at.com

和 WebGL 一樣,使用 RGBA 的格式,每個分量為 0 到 1 的范圍,比如 { r: 1, g: 0, b: 0, a: 1 } 表示紅色,或者你可以用數(shù)組的形式 [1, 0, 0, 1]。or428資訊網(wǎng)——每日最新資訊28at.com

const pass = encoder.beginRenderPass({  // 顏色附件,一個用于存儲渲染輸出顏色數(shù)據(jù)的紋理  colorAttachments: [    {      // 要渲染到的目標      view: ctx.getCurrentTexture().createView(),      // 渲染前清空顏色緩沖區(qū)      loadOp: 'clear',      // 清除顏色為淺藍色,不設(shè)置會默認使用黑色      clearValue: { r: 0.6, g: 0.8, b: 0.9, a: 1 },      // 渲染結(jié)果會被保留在紋理中,后序好繪制到 canvas 上      storeOp: 'store',    },  ],});

我們先不繪制三角形,看看背景的渲染效果,為此我們提前執(zhí)行下面代碼:or428資訊網(wǎng)——每日最新資訊28at.com

// 這里是繪制三角形的代碼,之后會實現(xiàn)pass.end(); // 完成指令隊列的記錄const commandBuffer = encoder.finish(); // 結(jié)束編碼device.queue.submit([commandBuffer]); // 提交給 GPU 命令隊列

or428資訊網(wǎng)——每日最新資訊28at.com

遠峰藍。or428資訊網(wǎng)——每日最新資訊28at.com

創(chuàng)建緩沖區(qū)

先說說 WebGPU 的坐標系,它和 WebGL 一樣,原點在畫布中心,x 軸向右,y 軸向上,取值范圍都是 -1 到 1。or428資訊網(wǎng)——每日最新資訊28at.com

or428資訊網(wǎng)——每日最新資訊28at.com

聲明頂點數(shù)據(jù)。這些頂點為組成三角形的三個坐標。or428資訊網(wǎng)——每日最新資訊28at.com

const vertices = new Float32Array([  -0.5, -0.5,  0.5, -0.5,  0.5, 0.5,]);

然后創(chuàng)建頂點緩沖區(qū):or428資訊網(wǎng)——每日最新資訊28at.com

const vertexBuffer = device.createBuffer({  // 標識,字符串隨意寫,報錯時會通過它定位  label: 'Triangle Vertices',  // 緩沖區(qū)大小,這里是 24 字節(jié)。6 個 4 字節(jié)(即 32 位)的浮點數(shù)  size: vertices.byteLength,  // 標識緩沖區(qū)用途(1)用于頂點著色器(2)可以從 CPU 復(fù)制數(shù)據(jù)到緩沖區(qū)  usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,});

label 方便我們定位錯誤位置:or428資訊網(wǎng)——每日最新資訊28at.com

or428資訊網(wǎng)——每日最新資訊28at.com

接著是將頂點數(shù)據(jù)復(fù)制到緩沖區(qū):or428資訊網(wǎng)——每日最新資訊28at.com

device.queue.writeBuffer(vertexBuffer, /* bufferOffset */ 0, vertices);

參數(shù) bufferOffset 表示緩沖區(qū)偏移多少字節(jié)數(shù)的位置寫入數(shù)據(jù)。or428資訊網(wǎng)——每日最新資訊28at.com

讀取方式

設(shè)置緩沖區(qū)的讀取方式。or428資訊網(wǎng)——每日最新資訊28at.com

const vertexBufferLayout = {  // 每組讀 8 個字節(jié)。一個坐標為兩個浮點數(shù)(2 * 4字節(jié))  arrayStride: 2 * 4,   attributes: [    {      // 指定數(shù)據(jù)格式,這樣 WebGPU 才知道該如何解析,格式為 2 個 32位浮點數(shù)      format: 'float32x2',      offset: 0, // 從每組的第一個數(shù)字開始      shaderLocation: 0, // 頂點著色器中的位置    },  ],};

attributes 是一個數(shù)組,這里我們只有頂點要讀,所以只有一個數(shù)組元素。如果引入了顏色值并和頂點放在一起,我們就要多聲明一個數(shù)組元素,并將 offset 指定到顏色的位置。or428資訊網(wǎng)——每日最新資訊28at.com

這個對象此時還沒用到,后面設(shè)置渲染流水線時會用到。or428資訊網(wǎng)——每日最新資訊28at.com

著色器

聲明 WebGPU 的著色器,創(chuàng)建著色器模塊(GPUShaderModule)。or428資訊網(wǎng)——每日最新資訊28at.com

WebGPU 使用特有的 WGSL 著色器語言,頂點著色器和片元著色器可以寫在一起的。or428資訊網(wǎng)——每日最新資訊28at.com

// 創(chuàng)建著色器模塊const vertexShaderModule = device.createShaderModule({  label: 'Vertex Shader',  code: `    @vertex    fn vertexMain(@location(0) pos: vec2f) -> @builtin(position) vec4f {      return vec4f(pos, 0, 1);    }    @fragment    fn fragmentMain() -> @location(0) vec4f {      return vec4f(1, 0, 0, 1);    }  `,});

頂點著色器函數(shù)。or428資訊網(wǎng)——每日最新資訊28at.com

@vertex fn vertexMain(@location(0) pos: vec2f) -> @builtin(position) vec4f {  return vec4f(pos, 0, 1);}
  • @vertex:裝飾器,表示頂點著色器主函數(shù)。
  • @location(0):緩沖區(qū)讀取方式設(shè)置的 shaderLocation,這里拿到了兩個浮點數(shù)。
  • vec2f:兩個浮點數(shù)的向量,同理,vec4f 為 4 浮點數(shù)的向量。
  • -> @builtin(position):表示函數(shù)的返回值會被設(shè)置為內(nèi)置的頂點位置變量。WebGPU 是利用函數(shù)的返回值配合修飾符的方式進行內(nèi)部變量賦值的。

片元著色器。or428資訊網(wǎng)——每日最新資訊28at.com

@fragmentfn fragmentMain() -> @location(0) vec4f {  return vec4f(1, 0, 0, 1); // 紅色}
  • @fragment 表示片元著色器主函數(shù)。
  • -> @location(0) 表示將返回的顏色輸出到位置為 0 的顏色附件上,簡單來說,就是給對應(yīng)點設(shè)置為對應(yīng)顏色。

渲染流水線

創(chuàng)建渲染流水線,也就是把之前的設(shè)置組合起來,用哪個著色器的哪個函數(shù)作為入口、如何讀取緩沖區(qū)等。or428資訊網(wǎng)——每日最新資訊28at.com

const pipeline = device.createRenderPipeline({  label: 'pipeline', // 標識,定位錯誤用  layout: 'auto', // 自動流水線布局  vertex: {    module: vertexShaderModule, // 著色器模塊    entryPoint: 'vertexMain', // 入口函數(shù)為 vertexMain    buffers: [vertexBufferLayout], // 讀取緩沖區(qū)的方式  },  fragment: {    module: vertexShaderModule,    entryPoint: 'fragmentMain',    targets: [      {        format: canvasFormat, // 輸出到 canvas 畫布上      },    ],  },});

將渲染流水線設(shè)置到 pass 上。or428資訊網(wǎng)——每日最新資訊28at.com

pass.setPipeline(pipeline);

將緩沖區(qū)綁定到管線的第一個頂點緩沖槽(slot)。or428資訊網(wǎng)——每日最新資訊28at.com

pass.setVertexBuffer(0, vertexBuffer);

繪制圖元,這里要設(shè)置繪制幾組,一組是兩個點,所以要處以 2。or428資訊網(wǎng)——每日最新資訊28at.com

pass.draw(vertices.length / 2);

然后就是前面講過的收尾代碼。or428資訊網(wǎng)——每日最新資訊28at.com

pass.end(); // 完成指令隊列的記錄const commandBuffer = encoder.finish(); // 結(jié)束編碼device.queue.submit([commandBuffer]); // 提交給 GPU 命令隊列

至此,一個三角形就畫好了。or428資訊網(wǎng)——每日最新資訊28at.com

繪制結(jié)果

or428資訊網(wǎng)——每日最新資訊28at.com

完整代碼

線上 demo 演示:or428資訊網(wǎng)——每日最新資訊28at.com

https://codesandbox.io/s/lg4w27?file=/src/index.mjs。or428資訊網(wǎng)——每日最新資訊28at.com

完整代碼:or428資訊網(wǎng)——每日最新資訊28at.com

const render = async () => {  const adapter = await navigator.gpu.requestAdapter();  const device = await adapter.requestDevice();  const canvas = document.querySelector('canvas');  const ctx = canvas.getContext('webgpu');  const canvasFormat = navigator.gpu.getPreferredCanvasFormat();  ctx.configure({    device,    format: canvasFormat,  });  const encoder = device.createCommandEncoder();  const pass = encoder.beginRenderPass({    colorAttachments: [      {        view: ctx.getCurrentTexture().createView(),        loadOp: 'clear',        clearValue: { r: 0.6, g: 0.8, b: 0.9, a: 1 },        storeOp: 'store',      },    ],  });  // 創(chuàng)建頂點數(shù)據(jù)  // prettier-ignore  const vertices = new Float32Array([    -0.5, -0.5,    0.5, -0.5,    0.5, 0.5,  ]);  // 緩沖區(qū)  const vertexBuffer = device.createBuffer({    // 標識,字符串隨意寫,報錯時會通過它定位,    label: 'Triangle Vertices',    // 緩沖區(qū)大小,這里是 24 字節(jié)。6 個 4 字節(jié)(即 32 位)的浮點數(shù)    size: vertices.byteLength,    // 標識緩沖區(qū)用途(1)用于頂點著色器(2)可以從 CPU 復(fù)制數(shù)據(jù)到緩沖區(qū)    // eslint-disable-next-line no-undef    usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,  });  // 將頂點數(shù)據(jù)復(fù)制到緩沖區(qū)  device.queue.writeBuffer(vertexBuffer, /* bufferOffset */ 0, vertices);  // GPU 應(yīng)該如何讀取緩沖區(qū)中的數(shù)據(jù)  const vertexBufferLayout = {    arrayStride: 2 * 4, // 每一組的字節(jié)數(shù),每組有兩個數(shù)字(2 * 4字節(jié))    attributes: [      {        format: 'float32x2', // 每個數(shù)字是32位浮點數(shù)        offset: 0, // 從每組的第一個數(shù)字開始        shaderLocation: 0, // 頂點著色器中的位置      },    ],  };  // 著色器用的是 WGSL 著色器語言  const vertexShaderModule = device.createShaderModule({    label: 'Vertex Shader',    code: `      @vertex      fn vertexMain(@location(0) pos: vec2f) -> @builtin(position) vec4f {        return vec4f(pos, 0, 1);      }      @fragment      fn fragmentMain() -> @location(0) vec4f {        return vec4f(1, 0, 0, 1);      }    `,  });  // 渲染流水線  const pipeline = device.createRenderPipeline({    label: 'pipeline',    layout: 'auto',    vertex: {      module: vertexShaderModule,      entryPoint: 'vertexMain',      buffers: [vertexBufferLayout],    },    fragment: {      module: vertexShaderModule,      entryPoint: 'fragmentMain',      targets: [        {          format: canvasFormat,        },      ],    },  });  pass.setPipeline(pipeline);  pass.setVertexBuffer(0, vertexBuffer);  pass.draw(vertices.length / 2);  pass.end();  const commandBuffer = encoder.finish();  device.queue.submit([commandBuffer]);};render();

結(jié)尾

本文講解了如何用 WebGPU 繪制一個三角形。可以看到它和 WebGL 的邏輯有很多共同之處的,都要創(chuàng)建緩沖區(qū)、著色器、定義讀取方式。or428資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-16287-0.htmlWebGPU 入門:繪制一個三角形

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 掌握Spring事件監(jiān)聽器的內(nèi)部邏輯與實現(xiàn)

下一篇: 轉(zhuǎn)轉(zhuǎn)Flutter實踐之路

標簽:
  • 熱門焦點
Top 主站蜘蛛池模板: 枞阳县| 丹巴县| 德州市| 买车| 芮城县| 家居| 黎平县| 石台县| 长阳| 琼海市| 绥化市| 红原县| 新营市| 渑池县| 西充县| 西城区| 隆化县| 潍坊市| 桂东县| 扶沟县| 林州市| 上林县| 西平县| 林甸县| 新绛县| 荃湾区| 县级市| 荔浦县| 清水河县| 徐水县| 淳安县| 广宗县| 巫溪县| 唐海县| 兴文县| 洮南市| 乐至县| 雷山县| 岗巴县| 江安县| 巴中市|