如何快速將大量遊戲對象呈現給玩家?本文將介紹一種通過GPU實現骨骼動畫的實例化繪製方法,並簡述其實現原理。
有多種通過GPU實現骨骼動畫的實例化繪製方法,本文介紹的是其中的一種:將頂點信息逐幀寫入紋理後,在頂點著色器中通過讀取動畫紋理,提取頂點位置並變換,最終實現角色動畫的方法。
本文將簡述其實現原理,並分享一個(完成了一半的)網格合並及實例化繪製工具。
如何提高繪製效率
當產生了“要將大量遊戲對象呈現給玩家”的需求時,我們就會碰到這樣一個問題:如何才能提高GPU的繪製效率?

批量繪製較多的騎兵
通常情況下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、合並多個不同的網格
手動調用實例化繪製接口時,隻能傳入一個網格。而我們平時使用的遊戲對象,通常是由若幹個蒙皮網格和若幹個普通網格組成。比如一個騎兵模型:士兵和馬匹分別是兩個蒙皮網格;而士兵手持的武器通常是一個普通網格,以方便後期做武器替換。

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

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

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

為戰士和戰馬分別替換貼圖
4、動畫的混合
通過紋理實現的動畫也可以實現簡單的混合效果,它是通過在頂點著色器中對多個動畫紋理進行采樣,然後根據一個混合比例,對多個位置信息進行計算以實現的。

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

脫離了GameObject+Renderer的繪製
使用動畫紋理的優缺點
優點
1、易於理解、易於實現;
2、CPU的計算(合並網格、記錄動畫信息)發生在編輯器階段,遊戲運行時CPU沒有額外的開銷;
3、可以實現實例化繪製,充分發揮GPU的繪製效率。
缺點
1、記錄頂點動畫的紋理大小,一方麵取決於模型的頂點數量,另一方麵取決於動畫的長度,如果頂點數量過多,或動畫過長,生成的紋理就會很大,對顯存的占用量也會上升;
2、實現動畫混合,需要從多個動畫紋理中采樣並進行計算,采樣次數多;
3、無法使用動畫狀態機控製動作;
4、動作信息在存儲時會受保存格式的精度影響,因此讀取出來的動畫可能不夠精確;
5、無法實現骨骼動畫中的IK(反向動力學)等。
雖然有不少缺點,但是如果你的目的是大批量繪製環境裝飾(樹、草、石頭)或細節要求不高的雜魚小兵、路人,它都是你實現目的優秀手段,值得你去使用它。
最後
最後,分享一個沒有寫完的網格合並及實例化繪製工具,可以實現上述簡單的功能。

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

簡單的動畫播放

大批攜帶動畫角色的實例化繪製
工具及Demo下載地址:
https://github.com/elsong823/AnimationBaker
來源:公眾號“偶爾學學Unity”
上一篇 如何在平台遊戲中實現走路與跳躍?
熱門課程
專業講師指導 快速擺脫技能困惑相關文章
多種教程 總有一個適合自己專業問題谘詢
你擔心的問題,火星幫你解答《黑神話:悟空》PS5遊戲版號已拿到!這幾個專業,未來真的賺到了!
一款像《黑神話:悟空》這樣的3A製作,其製作團隊一般需要遊戲策劃、原畫設計、3D美工、建模師、動畫師、UI製作、遊戲程序員等必要崗位。
今年回暖的行業不僅僅是動畫,像前麵提到的所有專業,如剪輯包裝、3D模型、角色動畫、虛幻視效、AI商業繪畫、虛幻開發、室內設計等......
騰訊遊戲美術春招已經啟動啦!25、26、27、28屆均有機會投遞,實習崗位有動畫、特效、3D場景/角色、2D場景/角色、技術美......
剛入學的同學,在第一次學到新軟件的時候,經常會被各種快捷鍵整懵。但是網上download的快捷鍵記憶圖,不一定就是合適的。這裏......
官方宣布3月共134款新遊戲版號過審,網傳68家遊戲公司超1000崗位
提前了解行業趨勢,關注熱門遊戲和新技術如AI、UE5等;或通過了解和參加線上、線下行業遊戲大賽或交流峰會,增加行業信息來源,積......
今天要分享的是武漢校區剪包專場招聘會,企業代表是曾就讀於武漢校區2210期的剪包學長,這次代表公司返校,招聘視頻拍攝剪輯崗位。