前往
大廳
主題 達人專欄

淺談Batching優化、GPU instance 與 Compute Shader製作草地、粒子模擬 製作紀錄

%%鼠 拒收病婿 | 2022-08-21 16:08:08 | 巴幣 2342 | 人氣 833

前言:
可以說是臨時專案需求,原本平凸凸的地形覺得過於單調,當下時間也滿趕的,所以直接把當時正在弄的GPU instance專案腳本移過來使用。
成品草地:
 
做好玩的粒子模擬:
這個之前文章有稍微提過,並非使用正統粒子模擬,而是使用深度檢測,效果並不好,最近有空會讀粒子模擬的教學,不過完整概念篇幅有點長,實作要等研究所推甄結束了。
 
建議先備知識:Compute shader製作經驗
 
文章一樣可以在個人網站觀看。


Draw Call 優化

在定義Draw Call是什麼之前,先淺談一下Unity的Batching原理會比較好理解。 Unity Batching分為靜態(static)與動態(dynamic)兩種。在處理渲染之前,會透過Batching的動作盡可能將同個材質的物件之網格合併成一個大的網格資料,如此減少Draw call。(若不合併,則一個mesh就會自己吃掉一個Draw call)

Static Batching

可以在inspector看到Static選項,藉此預先設為Static Batching物件,Unity在渲染時就會將同個材質球的物件網格結合成一個大的網格資料,也因此會增加記憶體的需求。

Dynamic Batching

原理同靜態batching,但是對於未預先設為static物件做的處理,且要求物件網格小於300面以下才行。
 
實做:
Draw Batch的方法:
[3]

定義與裝入資料

呼叫渲染

如此會有個問題: (1) DrawMeshInstanced每draw call限制1023個網格、(2)物件的transform資訊得藉由CPU去計算。
 

GPU Instance

為了繞過DrawMeshInstanced 1023個的限制,可以改使用DrawMeshInstancedIndirect 。
(官方Demo)

使用起來算是簡單,(除非參數有改,否則buffer設定一次即可)。

UpdateBuffers用於填入所需資料進ComputeBuffer 與材質buffer


其中參數比較難理解的是argsBuffer
定義:The GPU buffer containing the arguments for how many instances of this mesh to draw.
內容:Buffer with arguments, bufferWithArgs, has to have five integer numbers at given argsOffset offset: index count per instance, instance count, start index location, base vertex location, start instance location.
必須為長度5的陣列,內容分別為 三角形數量 , instance數量 , 三角形起始index , 頂點索引的偏移 , instance初始位置
通常這樣設定即可:

同理,草地僅是將position的y軸高度設為較小的範圍,使草貼齊地面。
100k面草 100fps,更好的做法是再搭配視錐剔除[5]( 又是個大坑...)。
以上我們透過DrawMeshInstancedIndirect解決了第一個限制的問題,接下來要將移動邏輯交給GPU去解決。

在Compute Shader處理移動

由於我這個是用深度貼圖進行移動判斷,該作法成效不好且也不太正確,但有助於初學Compute Shader。

流程圖

(為求簡化,所以假設Buffer只須設定一次)
 
主要的Loop邏輯如下:
這裡使用兩種shader,一個是compute shader叫做pointOffsetCS,專門用來算粒子位移;另一個un-lit shader用來製作渲染用的材質球。

由於球體會一直生成,所以得持續做buffer更新

簡單的直線掉落:

材質shader:
透過buffer資料直接改變vertex的世界位置,再投影到viewport空間。跟以往差別在於一個是從object-> view;一個是從world->view空間。


Compute shader做深度判斷

先取得screen的uv,並採樣周遭的深度

使用point的世界座標反推在畫面上的座標:

移動規則簡單檢查哪個方位可以移動,就往那個方向去。

透過材質球渲染出一個容易做遮罩的顏色:
透過Metaball材質與一些blending做出水的感覺:
 
最終:
原本是以為只要有兩個角度的深度圖就能大略做到3維的移動,但似乎對移動規則的參數很吃調整,且GPU也不太適合拿去做規則庫的運算(?),正確的做法還是得去學粒子模擬演算法,在此紀錄與提供一個經驗,僅供參考。


 

參考文獻與推薦閱讀
  1. Rendering 19, GPU Instancing, a Unity Tutorial (catlikecoding.com)
  2. 【unity掃盲】什麼是Draw Call? 什麼是Batch?_一片的博客-CSDN博客
  3. Unity GPU Instancing in less than 7 minutes! - YouTube
  4. 在Unity中使用DrawMeshInstanced和DrawMeshInstancedIndirect批量绘制网格 - 知乎 (zhihu.com)
  5. Unity实现GPUDriven地形 - 知乎 (zhihu.com)
 

送禮物贊助創作者 !
0
留言

創作回應

克萊
好用心!推推!
2022-08-21 16:14:55
%%鼠 拒收病婿
謝謝!!! ><
2022-08-21 22:39:17
⊰⊱求出處學術用⊰⊱
Compute shader跟一般 render pipeline 差在哪? 目前只有學一點render pipeline 常看到compute shader 做很特別的特效
好奇想問下
2022-08-22 05:18:57
%%鼠 拒收病婿
常見的shader可以分為pixel, vertex, geometry shaders等,他們的功能就寫在名字上;而ComputeShader並不只侷限於"著色"這件事,他可以拿來協助運算,例如上面的範例,若我粒子有好幾萬顆,顯然不可能讓CPU去一顆一顆算移動,所以我把資料Dispatch給GPU,請GPU照著ComputeShader的內容幫我做運算。

我對CS的想法是,就如同用到AI時會從C#呼叫Python函式庫;用到GPU時會需要呼叫CS,各司其職。

((我想其他種類的shader也是"繼承"於CS,例如文章中的粒子位置也可以在頂點著色器算,但頂點著色器要求一定要輸出什麼比較麻煩。
2022-08-22 12:30:04
⊰⊱求出處學術用⊰⊱
謝謝大大
2022-08-22 18:20:47
yorube
文章很用心 推推 : )
雖然是舊文嘞但看到錯誤還是想更正一下 Compute shader跟Shader沒有繼承關係
只是不同用途的Shader(可以想像成GPU專用的腳本格式只是目的不同 ) 但都是使用Shader Language

一般Shader用來在特定render pipeline stage讓你可以自訂義內容的部分如Geometry,Vert,Frag

CS 則是專門利用GPU做平行特性來計算用的Shader所以定義kernal時同常前面會讓你定義numthreads(線程組的大小)Unity開放讓你不一定需要定義 讓你分配並行計算的Group大小作最佳化(其它解釋篇幅太長)

簡單說我理解普通Shader你就知道這些固定stage要操作那些資料與傳遞順序主要是vert與frag 有簡單範例就能大致理解基本

而CS算是比較進階讓你直接調用GPU來執行特定操作 但你需要更理解GPU運行邏輯
2024-03-09 05:28:30
%%鼠 拒收病婿
感謝補充 [e16]
2024-03-10 00:59:03
追蹤 創作集

作者相關創作

更多創作