網路上的三角形 抓取一些東西
本系列介紹 WebGPU 與一般電腦圖形學。
首先讓我們看看我們要建造什麼,
生命遊戲
3D 渲染
3D 渲染,但有燈光
渲染 3D 模型
除了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上的程式。著色器有一種特殊的程式語言,我們稍後會討論。
渲染管有以下步驟,
- CPU 將資料載入到 GPU 中。 CPU可能會移除一些不可見的物體以節省GPU資源。
- CPU 設定 GPU 渲染場景所需的所有顏色、紋理和其他資料。
- CPU 觸發對 GPU 的繪製呼叫。
- GPU從CPU取得資料並開始渲染場景。
- GPU 運行到幾何進程,該進程處理場景的頂點。
- 在幾何過程中,第一步是頂點著色器,它處理場景的頂點。它可能會變換頂點,改變頂點的顏色,或對頂點做其他事情。
- 下一步是曲面細分著色器,它處理場景的頂點。它對頂點進行細分,其目的是增加場景的細節。它的程序也很多,但是太複雜了,無法在這裡解釋。
- 下一步是幾何著色器,它處理場景的頂點。與頂點著色器相比,開發人員只能定義如何變換一個頂點,而幾何著色器可以定義如何變換多個頂點。它還可以創建新的頂點,新的頂點可用於建立新的幾何體。
- 幾何處理的最後一步包括裁剪,去除超出螢幕的多餘部分,以及剔除,去除相機不可見的不可見部分。
- 下一步是光柵化過程,將頂點轉換為片段。片段是將要在螢幕上渲染的像素。
- 下一步是三角形迭代,即迭代場景的三角形。
- 下一步是片段著色器,它處理場景的片段。它可能會改變片段的顏色,改變片段的紋理,或對片段做其他事情。在這一部分中,還進行了深度測試和模板測試。深度測試是指為每個片段賦予深度值,深度值最小的片段將被渲染。 Stencil測試是指為每個fragment賦予stencil值,透過stencil測試的fragment將被渲染。模板值由開發者決定。
- 下一步是混合過程,混合場景的片段。例如,如果兩個片段重疊,則混合過程會將兩個片段混合在一起。
- 最後一步是輸出過程,將碎片輸出到交換鏈。交換鍊是用於渲染場景的影像鏈。更簡單地說,它是一個緩衝區,用於保存將要在螢幕上顯示的圖像。
根據圖元(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中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

JavaScript是現代Web開發的基石,它的主要功能包括事件驅動編程、動態內容生成和異步編程。 1)事件驅動編程允許網頁根據用戶操作動態變化。 2)動態內容生成使得頁面內容可以根據條件調整。 3)異步編程確保用戶界面不被阻塞。 JavaScript廣泛應用於網頁交互、單頁面應用和服務器端開發,極大地提升了用戶體驗和跨平台開發的靈活性。

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務
