當前位置:首頁 >教程首頁 > 华体会hth体育app在线登录 > 遊戲動作設計師班 >如何快速大量繪製遊戲對象?GPU實現骨骼動畫的實例化繪製方法

如何快速大量繪製遊戲對象?GPU實現骨骼動畫的實例化繪製方法

發布時間:2019-10-18 09:44:17

如何快速將大量遊戲對象呈現給玩家?本文將介紹一種通過GPU實現骨骼動畫的實例化繪製方法,並簡述其實現原理。

有多種通過GPU實現骨骼動畫的實例化繪製方法,本文介紹的是其中的一種:將頂點信息逐幀寫入紋理後,在頂點著色器中通過讀取動畫紋理,提取頂點位置並變換,最終實現角色動畫的方法。

本文將簡述其實現原理,並分享一個(完成了一半的)網格合並及實例化繪製工具。

如何提高繪製效率

當產生了“要將大量遊戲對象呈現給玩家”的需求時,我們就會碰到這樣一個問題:如何才能提高GPU的繪製效率?

1.webp.jpg

批量繪製較多的騎兵

通常情況下CPU對GPU發起的繪製命令,才是性能的瓶頸所在。CPU為繪製準備數據、顯存加載數據、為GPU設置渲染狀態等行為所花費的時間,通常比GPU繪製所花費的時間要多。這也就是為什麼我們經常會把DrawCall次數當成快速評判渲染效率的“KPI”。

反觀Unity提供的Static batching(靜態合批)和Dynamic batching(動態合批),也都是從減少CPU到GPU的調用次數為出發點,盡量一次發送一個大的網格(一大堆頂點數據),以減少CPU和GPU的通信次數,提高彼此的工作效率。

但是無論靜態還是動態合批,在大量遊戲對象繪製的需求麵前,都不太合適。

靜態合批從名字上就知道不能用來繪製移動物體,而且其本身還會產生非常大的內存開銷(它需要額外的內存空間來存儲合並的網格);動態合批也有自己的問題,如頂點數量的限製、材質球限製、無法作用於蒙皮網格(SkinnedMeshRenderer)等,還會對CPU產生不小的壓力(因為它要不停地去動態計算並合並網格)。

實例化繪製

實例化繪製技術的出現,就是為了在不提高CPU負擔的基礎之上,解決CPU到GPU調用開銷大的問題。對於相同的物體(同一個網格),隻需一次調用,GPU就會根據我們想要繪製的次數,啪啪啪一通畫,非常的高效。

但是簡單重複繪製一個物體多次(比如重複繪製1000次小兵),並沒有任何意義。為了能夠繪製出1000個不同的小兵,我們還需要提前為GPU準備一些額外的數據,比如1000個轉換矩陣(畫在不同的位置)、1000個混合色(呈現不同的顏色)等,最終在屏幕上呈現出千軍萬馬的畫麵。

如果我們想要在遊戲世界上呈現非常多相同的、靜止不動的石頭,那到此為止就可以了。我們使用Unity提供的手動實例化繪製接口Graphics.DrawMeshInstanced,通過傳入同一個石頭的網格和每一個石頭的轉換矩陣,就可以實現需求(其實Unity也會自動為添加了MeshRenderer組件的單位嚐試使用實例化繪製以提高效率)。

但是對戰場中的小兵做這種簡單地操作就不太合適了。

這是因為小兵通常是采用骨骼動畫來實現動作的,而骨骼動畫對於蒙皮網格的驅動,是CPU即時計算出來的。每個小兵相同時刻的狀態可能都不同,也就是說相同網格同一時刻的頂點位置會有很大差別,因此無法直接進行實例化繪製。

既然CPU上即時計算的骨骼動畫無法進行實例化繪製,我們就不讓CPU計算,而讓這些計算發生在GPU上,便可將問題解決。

它的原理很簡單

1、將骨骼動畫每一幀對網格各個頂點的變化結果存在一張紋理中,其中紋理的橫坐標是頂點索引,縱坐標是時間,而橫縱相交對應的值,是這一時刻該頂點在本地空間下的坐標。

2、有了這張“頂點動畫紋理”,在頂點著色器中,我們就可以忽視傳入頂點著色器的頂點位置信息;而以當前所處理的頂點索引為U,以動畫播放至此的時間刻度為V,從上一步的紋理坐標中采樣。而采樣到的結果,就是當前這個頂點此時的位置。

3、接下來的步驟便與傳統繪製一樣,與MVP矩陣相乘做空間變換,傳入片段著色器中著色等...可以很容易的想象到,連續為網格上所有頂點設置不同時間下的空間位置,最終繪製到屏幕上時,就能呈現出動畫效果了。

一些相對重要的細節

1、用實例化ID來獲取差異實例單位的屬性

由於我們的最終目標是繪製多個不同動畫狀態的單位,因此從動畫紋理中,用於采樣信息的時間刻度值,是根據實例化ID,從保存實例化屬性的數據塊中獲取到的,這樣就可以實現每個實例化單位的動畫播放進度的差異。

2、合並多個不同的網格

手動調用實例化繪製接口時,隻能傳入一個網格。而我們平時使用的遊戲對象,通常是由若幹個蒙皮網格和若幹個普通網格組成。比如一個騎兵模型:士兵和馬匹分別是兩個蒙皮網格;而士兵手持的武器通常是一個普通網格,以方便後期做武器替換。

2.webp.jpg

一個遊戲對象可能會由兩種、多個網格組合而成

因此我們會在編輯器模式下,將整個對象包含的網格合並成一個網格,並將這個網格保存成資源,以便後麵調用繪製命令時作為實參傳入。

3.webp.jpg

合並成為一個網格

3、多貼圖時處理UV

此外,有些模型上不同的網格還對應了不同的貼圖,比如網格Mesh_0,使用了貼圖Texture_0,網格Mesh_1使用了貼圖Texture_1,由於網格進行了合並,如果針對合並後的網格使用同一張貼圖,便會出現錯誤。

4.webp.jpg

胯下戰馬錯誤的顏色采樣

針對這種情況我們要在合並時做特殊處理,一種處理方式是合並多張貼圖,如將Texture_0與Texture_1合並,然後偏移原本Mesh_1的uv坐標,但是這要求兩張貼圖都不能太大,否則無法合並到一張貼圖中;另一種方法是仍然保留兩張貼圖Texture_0和Texture_1,但是對Mesh_0和Mesh_1的uv2做特殊處理,如使用uv2的x保存兩張貼圖的Lerp值。這樣片段著色器中對兩張貼圖的采樣結果做二次計算後,就可以得到正確的顏色了。

5.gif

為戰士和戰馬分別替換貼圖

4、動畫的混合

通過紋理實現的動畫也可以實現簡單的混合效果,它是通過在頂點著色器中對多個動畫紋理進行采樣,然後根據一個混合比例,對多個位置信息進行計算以實現的。

6.gif

根據速度一維向量進行的Locomotion狀態混合

5、脫離了Renderer的渲染

由於是直接調用了Graphics.DrawMeshInstanced進行的繪製,因此並沒有GameObject被創建出來,減少了對象的創建數量,一定程度上也減少了內存及CPU的開銷;但是需要自己在loop中組織數據的更新及渲染的更新。

7.webp.jpg

脫離了GameObject+Renderer的繪製

使用動畫紋理的優缺點

優點

1、易於理解、易於實現;

2、CPU的計算(合並網格、記錄動畫信息)發生在編輯器階段,遊戲運行時CPU沒有額外的開銷;

3、可以實現實例化繪製,充分發揮GPU的繪製效率。

缺點

1、記錄頂點動畫的紋理大小,一方麵取決於模型的頂點數量,另一方麵取決於動畫的長度,如果頂點數量過多,或動畫過長,生成的紋理就會很大,對顯存的占用量也會上升;

2、實現動畫混合,需要從多個動畫紋理中采樣並進行計算,采樣次數多;

3、無法使用動畫狀態機控製動作;

4、動作信息在存儲時會受保存格式的精度影響,因此讀取出來的動畫可能不夠精確;

5、無法實現骨骼動畫中的IK(反向動力學)等。

雖然有不少缺點,但是如果你的目的是大批量繪製環境裝飾(樹、草、石頭)或細節要求不高的雜魚小兵、路人,它都是你實現目的優秀手段,值得你去使用它。

最後

最後,分享一個沒有寫完的網格合並及實例化繪製工具,可以實現上述簡單的功能。

8.webp.jpg

通過工具生成動畫資源文件

9.gif

簡單的動畫播放

10.webp.jpg

大批攜帶動畫角色的實例化繪製

工具及Demo下載地址:

https://github.com/elsong823/AnimationBaker

來源:公眾號“偶爾學學Unity”

华体会hth体育网 賞析
  • 2101期學員李思庭作品

    2101期學員李思庭作品

  • 2104期學員林雪茹作品

    2104期學員林雪茹作品

  • 2107期學員趙淩作品

    2107期學員趙淩作品

  • 2107期學員趙燃作品

    2107期學員趙燃作品

  • 2106期學員徐正浩作品

    2106期學員徐正浩作品

  • 2106期學員弓莉作品

    2106期學員弓莉作品

  • 2105期學員白羽新作品

    2105期學員白羽新作品

  • 2107期學員王佳蕊作品

    2107期學員王佳蕊作品

專業問題谘詢

你擔心的問題,火星幫你解答
×

確定