【WebGPU】WebGPU 中的反應擴散計算著色器

在本教程中,我們將使用 WebGPU 技術中的計算著色器實現圖像效果。更多精彩內容盡在數字孿生平臺。

image.png

程序結構

主要構建兩個 WebGPU 管道:

  • 運行反應擴散算法多次迭代的計算管道(js/rd-compute.jsjs/shader/rd-compute-shader.js
  • 渲染管道,它獲取計算管道的結果并通過渲染全屏三角形(js/composite.jsjs/shader/composite-shader.js)來創建最終合成圖像。

WebGPU 是一個非常繁瑣的 API,為了使其更容易使用,我使用了 webgpu-utils 庫。此外,還包含 float16 庫,用于創建和更新計算管道的存儲紋理。

計算管道流程

在 GPU 上運行反應擴散模擬的一種常見方法是使用紋理交替。就是創建兩個紋理,一個紋理保存要讀取的模擬的當前狀態,另一個紋理存儲當前迭代的結果。每次迭代后,紋理都會交換。

此方法也可以使用片段著色器和幀緩沖在 WebGL 中實現。但是在 WebGPU 中,我們可以使用計算著色器和存儲紋理作為緩沖區來實現相同的效果。這樣做的優點是我們可以直接寫入我們想要的紋理內的任何像素,還獲得了計算著色器帶來的性能優勢。

初始化

首先是使用所有必要的布局描述符初始化管道。此外,還必須設置所有的緩沖區、紋理和綁定組。webgpu-utils 庫就可以在這里節省大量工作。

WebGPU 不允許在創建緩沖區或紋理后更改其大小。因此,我們必須區分大小不變的緩沖區(例如uniform)和在某些情況下發生變化的緩沖區(例如調整畫布大小時的紋理)。對于后者,我們需要一種方法來重新創建它們并在必要時銷毀舊的緩沖區。

用于反應擴散模擬的所有紋理都是畫布大小的一小部分(例如畫布大小的四分之一)。要處理的像素數量較少,可以釋放計算資源以進行更多迭代。因此,可以以相對較小的視覺損失進行更快的模擬。

除了“紋理交換”中涉及的兩個紋理之外,示例中還有第三個紋理,我將其稱為種子紋理。此紋理包含在其上繪制時鐘字母的 HTML 畫布的圖像數據。種子紋理用作反應擴散模擬的一種影響圖,以可視化時鐘字母。當 WebGPU 畫布調整大小時,必須重新創建該紋理以及相應的 HTML 畫布大小調整。

運行模擬

完成所有必要的初始化后,我們可以使用計算著色器實際運行反應擴散模擬。我們先回顧一下計算著色器的一些特性。

計算著色器的每次調用都會并行處理多個線程。線程數由計算著色器的工作組(workgroup)大小定義。著色器的調用次數由調度(dispatch)大小定義(線程總數 = 工作組大小 * 調度大小)。

這些值以三個維度指定。因此,并行處理 64 個線程的計算著色器可能如下所示:

@compute @workgroup_size(8, 8, 1) fn compute() {}

運行此著色器 256 次(即 16,384 個線程)需要如下的調度大小:

pass.dispatchWorkgroups(16, 16, 1);

反應擴散模擬要求我們處理紋理的每個像素。實現此目的的一種方法是使用 workgroup 大小為 1 和 dispatch大小等于像素總數(像是模仿片段著色器)。但是這樣不會提高性能,因為 workgroup 中的多個線程比單獨的調度更快。

另一方面,我們可能想到使用等于像素數的 workgroup 大小,并且僅調用一次(dispatch 大小為 1)。然而這是不可能的,因為最大 workgroup 大小是有限的。對于 WebGPU 的一般建議是選擇 workgroup 大小為 64。這要求我們將紋理內的像素數量劃分為 workgroup 大小(= 64 像素)的塊,并經常調度工作組以覆蓋整個紋理。

因此,現在我們有了 workgroup 大小的恒定值,并且能夠找到適當的 dispatch 大小來運行我們的模擬。但是其實我們還有更多可以優化的地方。

每線程像素數

為了使每個workgroup覆蓋更大的區域(更多像素),我們引入了圖塊大小。圖塊大小定義每個單獨線程處理的像素數,這就需要我們在著色器中使用嵌套 for 循環,所以我們需要保持圖塊大小非常小(例如 2×2)。

像素緩存

運行反應擴散模擬的一個重要步驟是與拉普拉斯核(3×3 矩陣)進行卷積。因此,對于我們處理的每個像素,我們必須讀取內核覆蓋的所有 9 個像素才能執行計算。由于像素與像素之間的內核重疊,因此會出現大量冗余紋理讀取。

幸運的是,計算著色器允許我們跨線程共享內存。所以我們可以創建像素緩存。這個方式(來自圖像模糊示例)是每個線程讀取其圖塊的像素并將它們寫入緩存。一旦workgroup的每個線程都將其像素存儲在緩存中(我們通過工作組屏障確保這一點),實際處理只需要使用從緩存中預取的像素。因此它不需要任何進一步的紋理讀取。計算函數的結構可能如下所示:

// workgroup所有線程共享的像素緩存
var<workgroup> cache: array<array<vec4f, 128>, 128>;@compute @workgroup_size(8, 8, 1)
fn compute_main(/* ...builtin variables */ ) {// 將此線程的圖塊的像素添加到緩存中for (var c=0u; c<2; c++) {for (var r=0u; r<2; r++) {// ... 從內置變量計算像素坐標// 將像素值存儲在緩存中cache[y][x] = value;}}// 在所有線程都到達此點之前不要繼續workgroupBarrier();// 處理該線程圖塊的每個像素for (var c=0u; c<2; c++) {for (var r=0u; r<2; r++) {// ... 執行反應擴散算法textureStore(/* ... */);}}}
}

但我們還必須注意另一個棘手的問題:內核卷積要求我們讀取比最終處理的像素更多的像素。我們可以擴展像素緩存大小,但是workgroup線程共享的內存大小限制為 16,384 字節。因此,我們必須將每一側的dispatch大小減少 (kernelSize - 1)/2。下面的插圖可以讓這些步驟更加清晰:
image.png

UV擾動

與片段著色器解決方案相比,使用計算著色器的一個缺點是無法在計算著色器中使用采樣器來存儲紋理(只能加載整數像素坐標)。如果想通過移動紋理空間(即以小數增量擾動 UV 坐標)來對模擬進行動畫處理,則必須自己進行采樣。

解決這個問題的一種方法是使用手動雙線性采樣函數。示例中使用的采樣函數基于此處所示的采樣函數,并進行了一些調整以供在計算著色器中使用。這允許我們對浮點像素值進行采樣:

fn texture2D_bilinear(t: texture_2d<f32>, coord: vec2f, dims: vec2u) -> vec4f {let f: vec2f = fract(coord);let sample: vec2u = vec2u(coord + (0.5 - f));let tl: vec4f = textureLoad(t, clamp(sample, vec2u(1, 1), dims), 0);let tr: vec4f = textureLoad(t, clamp(sample + vec2u(1, 0), vec2u(1, 1), dims), 0);let bl: vec4f = textureLoad(t, clamp(sample + vec2u(0, 1), vec2u(1, 1), dims), 0);let br: vec4f = textureLoad(t, clamp(sample + vec2u(1, 1), vec2u(1, 1), dims), 0);let tA: vec4f = mix(tl, tr, f.x);let tB: vec4f = mix(bl, br, f.x);return mix(tA, tB, f.y);
}

這就是示例中所示的從中心開始的模擬脈動運動的創建方式。

合成渲染

反應擴散模擬完成后,唯一剩下的就是將結果繪制到屏幕上。這是合成渲染管道的工作。

我這里簡要概述示例程序中涉及的步驟:

  1. 凸出變形:在對反應擴散結果紋理進行采樣之前,將凸出變形應用于 UV 坐標(基于此 Shadertoy 代碼),可以增加場景的深度感。
  2. 顏色:應用調色板(來自 Inigo Quilez)
  3. 浮雕濾鏡:簡單的浮雕效果賦予“紋理”一定的體積。
  4. 假彩虹色:這種微妙的效果基于不同的調色板,但應用于壓花結果的負空間。假虹彩使場景看起來更加充滿活力。
  5. 暈影:暈影疊加用于使邊緣變暗。

image.png

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/12979.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/12979.shtml
英文地址,請注明出處:http://en.pswp.cn/web/12979.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

react 渲染引擎經歷了那些迭代

React 渲染引擎經歷了多個迭代&#xff0c;主要集中在改進 Virtual DOM 的實現和優化渲染性能方面。以下是一些 React 渲染引擎的主要迭代&#xff1a; React Fiber 架構&#xff1a;React 16 引入了 Fiber 架構&#xff0c;這是一個重新實現的渲染引夠更好地支持異步渲染。 S…

script標簽以及defer和async屬性

1. <script>標簽 將JavaScript代碼嵌入到HTML中主要方式是使用<script>元素。 使用<script>的方式有兩種&#xff1a; &#xff08;1&#xff09;直接在網頁中嵌入JavaScript代碼&#xff1a; <script>function sayHi() {console.log("Hi"…

Leetcode—2244. 完成所有任務需要的最少輪數【中等】

2024每日刷題&#xff08;136&#xff09; Leetcode—2244. 完成所有任務需要的最少輪數 實現代碼 class Solution { public:int minimumRounds(vector<int>& tasks) {unordered_map<int, int> map;for(int task: tasks) {map[task];}int ans 0;// freq 1 …

【前端】CSS基礎(3)

文章目錄 前言1. CSS常用元素屬性1.1 字體屬性1.1.1 字體1.1.2 字體大小1.1.3 字體顏色1.1.4 字體粗細1.1.5 文字樣式 前言 這篇博客僅僅是對CSS的基本結構進行了一些說明&#xff0c;關于CSS的更多講解以及HTML、Javascript部分的講解可以關注一下下面的專欄&#xff0c;會持續…

c++父類指針指向子類

有一個常見的c題&#xff0c;就是父類和子類的構造函數和析構函數分別調用順序&#xff1a; 父類構造函數子類構造函數子類析構函數父類析構函數 以及父類中的函數在子類中重新實現后&#xff0c;父類指針指向子類后&#xff0c;該指針調用的函數是父類中的還是子類中的&…

震撼發布!GPT-4o 上線!

5 月 14日凌晨一點&#xff0c;OpenAI 發布了 GPT-4o&#xff01; 新模型的功能簡單概括就是&#xff1a;更快、更智能、更像人類。 秉承著持續更新的態度&#xff0c;Hulu AI 快速接入 GPT-4o 啦&#xff01; 繼 5 月份上線 Suno 之后&#xff0c;這次是 Hulu AI 的又一重大…

vue3專欄項目 -- 六、上傳組件(上)

1、上傳組件需求分析 我們還需要新建和展示文章&#xff0c;新建文章自然是發送post請求&#xff0c;同時在post中自帶對應的數據&#xff0c;展示文章就是根據id取出已有的數據并且展示出來。 這里有一個難點就是上傳組件&#xff0c;上傳文件是App應用中最基本的需求&#…

Socks5:網絡世界的隱形斗篷

在數字化時代&#xff0c;網絡隱私和安全已成為人們日益關注的話題。Socks5&#xff0c;作為一種代理協議&#xff0c;為用戶在網絡世界中的匿名性提供了強有力的支持。本文將從Socks5的多個方面&#xff0c;深入探討這一技術如何成為網絡世界的“隱形斗篷”。 一、Socks5的基本…

linux基礎指令講解(ls、pwd、cd、touch、mkdir)

&#x1fa90;&#x1fa90;&#x1fa90;歡迎來到程序員餐廳&#x1f4ab;&#x1f4ab;&#x1f4ab; 主廚&#xff1a;邪王真眼 主廚的主頁&#xff1a;Chef‘s blog 所屬專欄&#xff1a;c大冒險 總有光環在隕落&#xff0c;總有新星在閃爍 這個是我們今天要用到的初始…

P8805 [藍橋杯 2022 國 B] 機房

P8805 [藍橋杯 2022 國 B] 機房 分析 是一道lca題目&#xff0c;可以直接套模板 前綴和處理點權 具體思路&#xff1a; 1.n臺電腦用n-1條網線相連&#xff0c;任意兩個節點之間有且僅有一條路徑&#xff08;拆分成各自到公共祖先節點的路徑——lca&#xff09;&#xff1b;…

Delphi7:SuperObject 的示例

SuperObject 不是一個 Delphi 7 自帶或官方的庫&#xff0c;但可能是指一些開源的 JSON 解析庫&#xff0c;比如 superobject 或 dwscript 中的 SuperObject。這些庫通常用于解析和生成 JSON 數據。 以下是一個基于假設的 SuperObject 用法概述&#xff0c;因為不同的庫可能有…

波搜索算法(WSA)-2024年SCI新算法-公式原理詳解與性能測評 Matlab代碼免費獲取

? 聲明&#xff1a;文章是從本人公眾號中復制而來&#xff0c;因此&#xff0c;想最新最快了解各類智能優化算法及其改進的朋友&#xff0c;可關注我的公眾號&#xff1a;強盛機器學習&#xff0c;不定期會有很多免費代碼分享~ 目錄 原理簡介 一、初始化階段 二、全…

我與C++的愛戀:string類的常見接口函數

? ? &#x1f525;個人主頁&#xff1a;guoguoqiang. &#x1f525;專欄&#xff1a;我與C的愛戀 朋友們大家好啊&#xff0c;本節我們來到STL內容的第一部分&#xff1a;string類接口函數的介紹 ? ? 1.string類的認識 給大家分享一個c文檔 https://legacy.cplusplus.…

Weblogic 管理控制臺未授權遠程命令執行漏洞(CVE-2020-14882,CVE-2020-14883)

1 漏洞概述 Weblogic Pre-Auth Remote Command Execution 漏洞&#xff08;CVE-2020-14882, CVE-2020-14883&#xff09;是針對 Oracle WebLogic Server 的兩個安全漏洞。CVE-2020-14882 允許遠程用戶繞過管理員控制臺組件中的身份驗證&#xff0c;而 CVE-2020-14883 則允許經…

Python3 筆記:Python的函數

函數是編寫好的&#xff0c;可重復調用的&#xff0c;用來實現某一功能的一段代碼。 也可以理解為做某事的方法和步驟。第一次做的時候寫好了方法&#xff0c;下次同類型的事直接拿來就用。 Python 內部有很多功能強大的內置函數供我們使用&#xff0c;除此之外&#xff0c;你…

Nagle算法

Nagle算法簡介 Nagle算法主要是避免發送小的數據包&#xff0c;要求TCP連接上最多只能有一個未被確認的小分組&#xff0c;在該分組的確認到達之前不能發送其他的小分組。 在默認的情況下,Nagle算法是默認開啟的&#xff0c;Nagle算法比較適用于發送方發送大批量的小數據&…

Sam Blackshear談Move語言的起源

Move編程語言作為Sui生態系統的關鍵組成部分&#xff0c;通過可編程交易區塊等機制支持其獨特的對象數據模型&#xff0c;并支持高效的代碼。五年前&#xff0c;Mysten Labs的聯合創始人兼首席技術官Sam Blackshear創建了Move。他專門設計了Move&#xff0c;用于編寫智能合約&a…

數據庫SQL查詢語句匯總詳解

SQL是一種強大的編程語句&#xff0c;可用于操作和提取數據庫中的數據。如果你對編程語句有所讓步&#xff0c;那么你可能對SQL的力量感到難以置信。本文將帶你深入探索SQL查詢的世界&#xff0c;讓你了解SQL語句的各種查詢方式&#xff0c;并以實例進行詳解。 1. SELECT基礎查…

Iterator底層源碼分析

/** * Iterator用于遍歷Collection下的集合&#xff0c;Collection下的每個集合底層實現不一樣&#xff0c;意味著遍歷邏輯也不一樣&#xff0c; * 所以Java的設計者將Iterator設計成了接口&#xff0c;讓Collection下的每個集合實現Iterator */ public interface Iterator<…

英偉達的GPU(1)

又好久沒更新了,一方面是最近事情有點多,另一方面最近也確實有點懶。 之前我說要把硬件部分補完,要寫Nvidia的GPU,我估計一篇寫不完,所以先寫點。 早先的硬件文章可以參考: 上一篇:解讀神秘的華為昇騰910 (qq.com) 上上一篇Microsoft Maia (qq.com) 上上上篇Google的…