首頁 > web前端 > js教程 > 網路上的三角形 抓取一些東西

網路上的三角形 抓取一些東西

Patricia Arquette
發布: 2024-11-30 02:42:10
原創
150 人瀏覽過

本系列介紹 WebGPU 與一般電腦圖形學。

首先讓我們看看我們要建造什麼,

生命遊戲

Triangles On Web Chraw Something

3D 渲染

Triangles On Web Chraw Something

3D 渲染,但有燈光

Triangles On Web Chraw Something

渲染 3D 模型

Triangles On Web Chraw Something

除了JS基礎知識外,不需要任何基礎知識。

教學已經在我的 github 上完成,附有原始碼。

WebGPU 是一個相對較新的 GPU API。儘管名為 WebGPU,但它實際上可以被視為 Vulkan、DirectX 12、Metal、OpenGL 和 WebGL 之上的一層。它被設計為低階 API,旨在用於高效能應用程序,例如遊戲和模擬。

在本章中,我們將在螢幕上繪製一些東西。第一部分將參考 Google Codelabs 教學。我們將在螢幕上創建一個生活遊戲。

起點

我們將在啟用 typescript 的 vite 中建立一個空的普通 JS 專案。然後清除所有多餘的程式碼,只留下main.ts。

const main = async () => {
    console.log('Hello, world!')
}

main()
登入後複製
登入後複製
登入後複製
登入後複製

在實際編碼之前,請檢查您的瀏覽器是否啟用了 WebGPU。您可以在 WebGPU Samples 上查看它。

Chrome 現在預設為啟用狀態。在 Safari 上,您應該前往開發者設定、標記設定並啟用 WebGPU。

我們還需要為 WebGPU 啟用這些類型,安裝 @webgpu/types,並在 tsc 編譯器選項中加入 "types": ["@webgpu/types"]。

此外,我們替換了

畫一個三角形

WebGPU 有許多樣板程式碼,如下圖所示。

請求設備

首先我們需要存取 GPU。在WebGPU中,是透過適配器的概念來完成的,適配器是GPU和瀏覽器之間的橋樑。

const adapter = await navigator.gpu.requestAdapter();
登入後複製
登入後複製
登入後複製
登入後複製

然後我們需要向適配器請求一個設備。

const device = await adapter.requestDevice();
console.log(device);
登入後複製
登入後複製
登入後複製
登入後複製

配置畫布

我們在畫布上繪製三角形。我們需要取得canvas元素並配置它。

const canvas = document.getElementById('app') as HTMLCanvasElement;
const context = canvas.getContext("webgpu")!;
const canvasFormat = navigator.gpu.getPreferredCanvasFormat();
context.configure({
    device: device,
    format: canvasFormat,
});
登入後複製
登入後複製
登入後複製
登入後複製

這裡,我們使用 getContext 來取得畫布的相關資訊。透過指定 webgpu,我們將獲得一個負責使用 WebGPU 進行渲染的上下文。

CanvasFormat其實就是顏色模式,例如srgb。我們通常只使用首選格式。

最後,我們使用設備和格式來配置上下文。

了解 GPU 渲染管線

在深入研究工程細節之前,我們首先必須了解 GPU 如何處理渲染。

GPU 渲染管道是 GPU 渲染影像所採取的一系列步驟。

在 GPU 上執行的應用程式稱為著色器。著色器是運行在GPU上的程式。著色器有一種特殊的程式語言,我們稍後會討論。

渲染管有以下步驟,

  1. CPU 將資料載入到 GPU 中。 CPU可能會移除一些不可見的物體以節省GPU資源。
  2. CPU 設定 GPU 渲染場景所需的所有顏色、紋理和其他資料。
  3. CPU 觸發對 GPU 的繪製呼叫。
  4. GPU從CPU取得資料並開始渲染場景。
  5. GPU 運行到幾何進程,該進程處理場景的頂點。
  6. 在幾何過程中,第一步是頂點著色器,它處理場景的頂點。它可能會變換頂點,改變頂點的顏色,或對頂點做其他事情。
  7. 下一步是曲面細分著色器,它處理場景的頂點。它對頂點進行細分,其目的是增加場景的細節。它的程序也很多,但是太複雜了,無法在這裡解釋。
  8. 下一步是幾何著色器,它處理場景的頂點。與頂點著色器相比,開發人員只能定義如何變換一個頂點,而幾何著色器可以定義如何變換多個頂點。它還可以創建新的頂點,新的頂點可用於建立新的幾何體。
  9. 幾何處理的最後一步包括裁剪,去除超出螢幕的多餘部分,以及剔除,去除相機不可見的不可見部分。
  10. 下一步是光柵化過程,將頂點轉換為片段。片段是將要在螢幕上渲染的像素。
  11. 下一步是三角形迭代,即迭代場景的三角形。
  12. 下一步是片段著色器,它處理場景的片段。它可能會改變片段的顏色,改變片段的紋理,或對片段做其他事情。在這一部分中,還進行了深度測試和模板測試。深度測試是指為每個片段賦予深度值,深度值最小的片段將被渲染。 Stencil測試是指為每個fragment賦予stencil值,透過stencil測試的fragment將被渲染。模板值由開發者決定。
  13. 下一步是混合過程,混合場景的片段。例如,如果兩個片段重疊,則混合過程會將兩個片段混合在一起。
  14. 最後一步是輸出過程,將碎片輸出到交換鏈。交換鍊是用於渲染場景的影像鏈。更簡單地說,它是一個緩衝區,用於保存將要在螢幕上顯示的圖像。

根據圖元(GPU 可以渲染的最小單位)的不同,管道可能有不同的步驟。通常,我們使用三角形,它通知 GPU 將每 3 組頂點視為三角形。

建立渲染通道

Render Pass 是完整 GPU 渲染的一個步驟。建立渲染頻道後,GPU 將開始渲染場景,完成後反之亦然。

要建立渲染通道,我們需要建立一個編碼器,負責將渲染通道編譯為 GPU 程式碼。

const main = async () => {
    console.log('Hello, world!')
}

main()
登入後複製
登入後複製
登入後複製
登入後複製

然後我們建立一個渲染通道。

const adapter = await navigator.gpu.requestAdapter();
登入後複製
登入後複製
登入後複製
登入後複製

在這裡,我們建立一個帶有顏色附件的渲染通道。附件是 GPU 中的一個概念,表示將要渲染的影像。一張圖像可能有很多個方面需要 GPU 處理,每個方面都是一個附件。

這裡我們只有一個配件,就是顏色配件。視圖是 GPU 將在其上渲染的面板,這裡我們將其設定為畫布的紋理。

loadOp是GPU在渲染通道之前執行的操作,clear表示GPU將首先清除最後一幀之前的所有數據,storeOp是GPU在渲染通道之後執行的操作,store表示GPU將把數據儲存到紋理中。

loadOp可以是load,它保留最後一幀的數據,也可以是clear,它清除最後一幀的數據。 storeOp可以是store,資料儲存到紋理,也可以是discard,丟棄資料。

現在,只需呼叫 pass.end() 即可結束渲染通道。現在,該命令已保存在 GPU 的命令緩衝區中。

要取得編譯後的指令,請使用以下程式碼,

const device = await adapter.requestDevice();
console.log(device);
登入後複製
登入後複製
登入後複製
登入後複製

最後,將指令提交到 GPU 的渲染佇列。

const canvas = document.getElementById('app') as HTMLCanvasElement;
const context = canvas.getContext("webgpu")!;
const canvasFormat = navigator.gpu.getPreferredCanvasFormat();
context.configure({
    device: device,
    format: canvasFormat,
});
登入後複製
登入後複製
登入後複製
登入後複製

現在,您應該會看到一個醜陋的黑色畫布。

根據我們對 3D 的刻板印象,我們期望空白空間是藍色的。我們可以透過設定透明顏色來做到這一點。

const encoder = device.createCommandEncoder();
登入後複製

使用著色器繪製三角形

現在,我們將在畫布上繪製一個三角形。我們將使用著色器來做到這一點。著色器語言將是 wgsl,WebGPU 著色語言。

現在,假設我們要繪製一個具有以下座標的三角形,

const pass = encoder.beginRenderPass({
  colorAttachments: [{
     view: context.getCurrentTexture().createView(),
     loadOp: "clear",
     storeOp: "store",
  }]
});
登入後複製

正如我們之前所說,要完成渲染管道,我們需要一個頂點著色器和一個片段著色器。

頂點著色器

使用以下程式碼建立著色器模組。

const commandBuffer = encoder.finish();
登入後複製

這裡的label只是一個名稱,用來除錯。 code 是實際的著色器代碼。

頂點著色器是一個接受任意參數並傳回頂點位置的函數。然而,與我們的預期相反,頂點著色器會傳回一個四維向量,而不是一個三維向量。第四個維度是w維度,用於透視劃分。我們稍後再討論。

現在,您可以簡單地將四維向量 (x, y, z, w) 視為三維向量 (x / w, y / w, z / w)。

但是,還有一個問題-如何將資料傳遞給著色器,以及如何從著色器中取出資料。

為了將資料傳遞給著色器,我們使用 vertexBuffer,一個包含頂點資料的緩衝區。我們可以使用以下程式碼建立一個緩衝區,

const main = async () => {
    console.log('Hello, world!')
}

main()
登入後複製
登入後複製
登入後複製
登入後複製

這裡我們建立了一個緩衝區,大小是24位元組,6個浮點數,這是頂點的大小。

usage是緩衝區的使用情況,對於頂點資料來說就是VERTEX。 GPUBufferUsage.COPY_DST 表示該緩衝區可作為複製目標。對於所有由CPU寫入資料的緩衝區,我們需要設定這個標誌。

這裡的map是指將buffer映射到CPU,也就是說CPU可以對buffer進行讀寫操作。 unmap的意思是取消緩衝區的映射,這表示CPU不能再讀寫緩衝區,因此內容可供GPU使用。

現在,我們可以將資料寫入緩衝區。

const adapter = await navigator.gpu.requestAdapter();
登入後複製
登入後複製
登入後複製
登入後複製

這裡,我們將緩衝區映射到CPU,並將資料寫入緩衝區。然後我們取消映射緩衝區。

vertexBuffer.getMappedRange() 將會傳回對應到 CPU 的緩衝區範圍。我們可以用它來將資料寫入緩衝區。

但是,這些只是原始數據,GPU 不知道如何解釋它們。我們需要定義緩衝區的佈局。

const device = await adapter.requestDevice();
console.log(device);
登入後複製
登入後複製
登入後複製
登入後複製

在這裡,arrayStride是GPU在尋找下一個輸入時需要在緩衝區中向前跳過的位元組數。例如,如果 arrayStride 為 8,GPU 將跳過 8 個位元組來取得下一個輸入。

由於這裡我們使用float32x2,步幅是8個位元組,每個float 4個位元組,每個頂點2個float。

現在我們可以寫頂點著色器了。

const canvas = document.getElementById('app') as HTMLCanvasElement;
const context = canvas.getContext("webgpu")!;
const canvasFormat = navigator.gpu.getPreferredCanvasFormat();
context.configure({
    device: device,
    format: canvasFormat,
});
登入後複製
登入後複製
登入後複製
登入後複製

這裡,@vertex 表示這是一個頂點著色器。 @location(0) 表示屬性的位置,如前面定義的那樣,為 0。請注意,在著色器語言中,您正在處理緩衝區的佈局,因此每當您傳遞一個值時,您需要傳遞一個結構體,其欄位已定義@location,或僅傳遞一個帶有@location的值。

vec2f 是二維浮點向量,vec4f 是四維浮點向量。由於頂點著色器需要傳回 vec4f 位置,因此我們需要使用 @builtin(position) 對其進行註解。

片段著色器

片段著色器,類似地,是取得插值頂點輸出並輸出附件(在本例中為顏色)的東西。插值意味著雖然只有頂點上的某些像素具有確定的值,但對於每隔一個像素,這些值都會被插值,可以是線性的、平均的或其他方式。 fragment的顏色是一個四維向量,即fragment的顏色,分別是紅、綠、藍、alpha。

請注意,顏色的範圍是0到1,而不是0到255。此外,片段著色器定義的是每個頂點的顏色,而不是三角形的顏色。三角形的顏色由頂點的顏色透過內插法決定。

由於我們目前不想控製片段的顏色,所以我們可以簡單地回傳一個常數顏色。

const main = async () => {
    console.log('Hello, world!')
}

main()
登入後複製
登入後複製
登入後複製
登入後複製

渲染管線

然後我們透過取代頂點和片段著色器來定義自訂渲染管道。

const adapter = await navigator.gpu.requestAdapter();
登入後複製
登入後複製
登入後複製
登入後複製

注意,在片段著色器中,我們需要指定目標的格式,也就是畫布的格式。

抽獎

在渲染過程結束之前,我們先加入繪製呼叫。

const device = await adapter.requestDevice();
console.log(device);
登入後複製
登入後複製
登入後複製
登入後複製

這裡,在setVertexBuffer中,第一個參數是緩衝區的索引,在管道定義欄位buffers中,第二個參數是緩衝區本身。

呼叫draw時,參數是要繪製的頂點數。由於我們有 3 個頂點,因此我們繪製 3 個。

現在,您應該在畫布上看到一個黃色三角形。

繪製生命遊戲細胞

現在我們稍微調整一下程式碼 - 因為我們想要建立一個生活遊戲,所以我們需要繪製正方形而不是三角形。

正方形其實是兩個三角形,所以我們要畫6個頂點。這裡的改動很簡單,不需要詳細解釋。

const canvas = document.getElementById('app') as HTMLCanvasElement;
const context = canvas.getContext("webgpu")!;
const canvasFormat = navigator.gpu.getPreferredCanvasFormat();
context.configure({
    device: device,
    format: canvasFormat,
});
登入後複製
登入後複製
登入後複製
登入後複製

現在,您應該在畫布上看到一個黃色方塊。

座標系

我們沒有討論GPU的座標系。嗯,這相當簡單。 GPU實際的座標係是右手座標系,即x軸指向右側,y軸指向上方,z軸指向螢幕外。

座標系的範圍是-1到1。原點位於螢幕中心。 z軸從0到1,0是近平面,1是遠平面。然而,z 軸代表深度。當你做3D渲染時,你不能只使用z軸來決定物體的位置,你需要使用透視劃分。這稱為 NDC,標準化設備座標。

例如,要在螢幕左上角畫一個正方形,頂點為(-1, 1), (-1, 0), (0, 1), (0, 0) ,儘管你需要使用兩個三角形來繪製它。

以上是網路上的三角形 抓取一些東西的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板