20萬+骨骼動畫角色的優化【UE4】

最近我們的任務是製作一個需要實時支持數千個動畫角色的,同時還要爲場景中的多個動態燈光、密集粒子效果以及照片級環境和角色預留足夠的性能。最終我們實現了單一場景中超過20萬個動畫角色的流暢運行。

1、問題

任何使用過大量骨骼動畫組件的人都很可能會遇到性能問題。在撰寫本文時,UE4中沒有對實例化骨骼網格體的原生支持,因此每個角色至少會產生一個繪製調用的成本。最重要的是,在 CPU 上變形蒙皮幾何體本身就比僅僅提供一個良好的老式靜態網格體要慢。

因此,當我們開發這個系統時,決定將角色渲染爲實例化靜態網格體,並使用將動畫數據烘焙到紋理並在頂點着色器中對角色進行動畫處理的流行技術。

然而,這一決定帶來了一些新的問題。傳統上,對於骨骼網格體的每一幀動畫數據,都要將每個頂點位置烘焙到紋理中。這意味着動畫紋理和模型的拓撲之間存在直接關係。

在這裏插入圖片描述

由於我們需要支持多種角色變體,這意味着每個變體都有自己的拓撲,需要爲同一動畫單獨烘焙的頂點位置紋理。

更糟糕的是,每個LOD(細節級別)都需要自己獨特的動畫紋理。最終,我們得到了 19 個不同的動畫循環和 4 個唯一的角色(每個角色都有三個 LOD 狀態),因此,如果我們使用這種方法,則需要 228 個唯一的頂點動畫紋理。這需要大量的紋理內存,特別是當你考慮到數據需要採用未壓縮的HDR格式時,這個問題是在我們考慮使用其他紋理進行動畫混合之前,稍後會談到這一點!

2、解決方案

很明顯,我們需要一個更輕量級和靈活的解決方案,支持在具有不同拓撲結構的角色之間共享動畫數據。

早在2016年,我們在REWIND上爲一個未發佈的項目也做了類似的事情,其中角色由剛性的塊狀關節組成。這使我們能夠將每個關節視爲剛性對象,因此,我們只需要存儲每個關節的位置和旋轉值,而不是存儲每個頂點的位置值。

在這裏插入圖片描述

對於任何給定的動畫,我們將組件位置和每幀每個關節的旋轉寫入紋理。然後,我們在頂點着色器中對此紋理進行採樣,以置換頂點,從而解析每幀的每個姿態。

作爲一個前期過程,我們還將角色分成了單獨的四肢,並將它們的轉換歸零。這意味着每個肢體的樞軸與組件的樞軸共享,因此爲了解決姿態問題,我們可以圍繞組件的樞軸旋轉每個肢體,然後將其轉換爲位置。

以下是角色的幾何形狀未受干擾的樣子...

在這裏插入圖片描述

...下面是如何在頂點着色器中解析姿勢的步驟拆解。當然,在實踐中,在提供柵格化之前,每個幀都會立即解決此問題。 在這裏插入圖片描述

通過將動畫數據與拓撲分離,我們能夠將多個網格重新定位到同一個裝備,並讓它們使用相同的動畫數據。

在這裏插入圖片描述

另一個好處是,紋理的垂直度不是由網格中的頂點數定義的,而是由裝備中的關節數(乘以2,因爲我們需要一個條目來定位和旋轉)。幾乎可以肯定的是,關節計數將比頂點計數小得多。事實上,這個裝備只有16個關節,使動畫紋理高32像素,而不是等效的每頂點烘焙的大約2000像素高。 在這裏插入圖片描述

不幸的是,我們負責創建的角色系統不是由方塊人體組成的,因此需要調整這個系統以支持更自然的動畫角色。

3、爲頂點動畫構造靜態網格體

我們能夠通過設置一個約束來輕鬆地使此工作流程適應我們的需求。支持蒙皮網格的每個頂點的關節影響。

將平移和旋轉編碼爲綁定姿態的增量,並在網格的UV中序列化關節的綁定姿勢位置,而不是將動畫數據寫入組件空間並分離關節,這樣會更優雅 - 就像我們對方塊人體所做的那樣。這意味着我們的網格邊界框更能代表我們的角色,而不受干擾的網格只是一個T姿態。

我們編寫了一個腳本,用於評估蒙皮網格,並將其所有頂點設置爲受單個關節的影響。然後,對於每個頂點,腳本查詢單個影響關節在場景空間中的位置,並在給定邊界比例的情況下將此位置重新映射到 0 和 1 之間。我們確定的邊界尺度爲"200",因爲它足夠大,可以輕鬆地包含整個網格,但又不會大到引入精度誤差的程度。

然後,我們將此位置寫入兩個 UV 集的 UV 座標 — 出於本文的目的,讓我們將它們稱爲"A"和"B"。X 和 Y 場景位置分別寫入A UV 集的 U 和 V 座標,Z 場景位置寫入B UV 集的 U 座標。

在這裏插入圖片描述

額外的好處是,我們還獲得了UV視口內聯合層次結構的可視化效果!

然後保留 Z UV 集的 V 分量,以便將頂點組放置在其數據行中。 在這裏插入圖片描述

上面的示例顯示了位置數據中的頂點索引。要索引到旋轉數據中,只需從着色器中的 V 座標中減去 0.5。

然後,這個自動 Maya 程序可以應用於共享同一裝備的任何其他角色,以允許其使用相同的動畫數據。

在這裏插入圖片描述

設置了爲頂點動畫創作靜態網格的過程後,我們將注意力轉向了將動畫數據寫入紋理。

4、編寫動畫循環紋理

我們在UE4中將所有頂點動畫紋理生成爲離線編輯器程序。我們構建了一個簡單的藍圖,可以採用骨骼網格體和動畫數組,併爲每個動畫導出 16 位 HDR。

首先,我們以綁定姿態緩存關節的所有組件空間位置和旋轉數據,並啓動一個臨時渲染目標緩衝區,其尺寸由骨架中的關節數(高度)和動畫中的幀數(寬度)決定。

其次,我們按給定的幀速率(在本例中爲 30fps)擦除動畫數據,並計算每個關節的新組件空間位置和旋轉與我們在上一步中緩存的數據之間的增量。

然後,我們將此數據寫入渲染目標,其中每行表示一個關節,每列表示一幀動畫。旋轉數據被寫入紋理的上半部分,位置數據被寫入底部。將此數據寫入單個紋理會使交換動畫變得微不足道,因爲所有動畫數據存儲在單個圖像中。

在這裏插入圖片描述

我們爲動畫的每一幀編寫了一個新的數據列。用盡所有幀後,我們將渲染目標導出到磁盤。最後,一旦所有的動畫都烘焙好了,就能夠將它們導入回編輯器中,作爲標準的UTexture2D使用。

5、編寫頂點動畫着色器

一旦我們獲得了讓角色移動所需的所有數據,就必須編寫着色器來適當地解壓縮所有內容。

任何玩過Houdini的UE4或Unity頂點動畫流程的人可能已經熟悉本節中介紹的大部分概念。

6、動畫播放

我們通過標量參數將動畫位置饋送到材質中。AnimPosition被解釋爲秒。然後,我們按錄製動畫的幀速率 (FPS) 縮放此值,併爲動畫變化添加隨機偏移量,這一點稍後會詳細介紹!

最後,我們按動畫的長度縮放此值,以便可以索引到紋理的給定動畫位置的正確幀中。 在這裏插入圖片描述

7、讀取烘焙頂點動畫數據

接下來,我們需要製作一個 UV 以索引到頂點動畫紋理中,以便爲正在評估的頂點輸出正確的值。我們通過創建一個 Float2 來做到這一點,其中 X(或 U)是動畫位置,Y(或 V)是數據所在的行。

如前所述,我們將位置和旋轉數據存儲在單個紋理中。但是,在合併結果之前,需要對每個紋理執行單獨的紋理查找。 在這裏插入圖片描述

8、從UV通道解包關節樞軸

一旦我們查詢了正確的動畫數據,只需要用它來轉換頂點。我們從紋理中獲得了旋轉數據,但需要檢索旋轉頂點的數據。作爲 Maya 自動化過程的一部分,我們將其編碼到網格的 UV 中,因此只需將歸一化的 UV 座標轉換爲正確的比例即可。

請注意,Maya使用Y-Up座標系,而UE4使用Z-Up,這就是我們在MakeFloat3節點中交換這兩個值的原因。 在這裏插入圖片描述

接下來,我們解決了圍繞樞軸的四元數δ旋轉(轉換爲局部空間),並將其與平移δ的結果相結合。

在這裏插入圖片描述

最後,我們需要使用TransformVector節點將其從本地空間轉換爲世界空間,並將其傳遞到結果節點上的"世界位置偏移"引腳中,並且 - 成功!

在這裏插入圖片描述

一旦有了一個高性能的動畫角色,我們就可以使用虛幻的層級實例化靜態網格體組件來生成數千個實例。

9、變化

沒有什麼比20萬人完美地團結一致地移動更令人不安的了,所以我們很快就增加了一些變化。

我們大量使用實例隨機變量(在着色器中由實例化的靜態網格體組件提供)來進行其他變體。這是一個介於 0 和 1 之間的隨機值,對於網格的每個實例都是唯一的。我們使用此值在角色的不同比例之間進行插值,並對其進行了評估,也進行水平翻轉,以便在相同的繪製調用中產生更多變化。

最重要的是,我們用它來抵消動畫位置值,以便角色在不同的時間開始動畫循環。 在這裏插入圖片描述

這爲我們提供了相當多的單網格繪製調用。以犧牲更多的繪製調用爲代價,我們增加了一些角色變體。 在這裏插入圖片描述

對於其他變化,我們爲每個實例使用了不同的動畫循環。爲每個網格實例承擔了了唯一繪製調用的成本,那麼爲什麼不提供不同的動畫呢? 在這裏插入圖片描述

雖然這意味着同一類型的所有角色都將播放相同的動畫,但在我們的應用場景中這一點影響不大。

10、LOD

由於我們的頂點動畫紋理與頂點數量解耦,因此添加網格LOD支持就很簡單了 — 只需要爲Maya自動化程序提供低模,就得到了LOD。

在這裏插入圖片描述

對於最低LOD,考慮採用久經考驗的面向相機的精靈方法。但是,這帶來了一些問題:

  • 該技術無法很好地擴展到VR平臺,因爲當以立體方式渲染時,感覺會出錯。
  • 我們不侷限於從單一方向查看角色,因此需要採用"冒名頂替者"廣告牌方法來爲每個角色提供多個視角。
  • 角色仍然需要表達運動,這必須與完全實現的角色相匹配,因此我們需要一組動畫循環 - 全部來自多個角度 - 在巨大的精靈表中。
  • 密集的角色組會導致大量半透明過度繪製。

相反,我們在 Maya 中爲一個只有 70 個三角形的角色製作了一個"樣條曲線"表示,該角色使用的動畫數據與 LOD 0 角色完全相同。我們將寬度值編碼到頂點顏色的G通道中,並將其輸入到簡化版本的"SplineThicken"節點中,這使我們能夠將幾何體的面定向到相機。該效果並非沒有僞影,但在遠處保持良好狀態,最重要的是,在LOD狀態之間無縫混合,而無需任何額外的動畫邏輯/數據。 在這裏插入圖片描述

11、未來工作與系統擴展

我們還實現了一個混合系統,以允許在運行時在循環狀態之間平滑轉換。作爲UE4中的離線流程,骨骼網格體在兩種動畫狀態之間執行程序動畫混合,並將其烘焙成定製的一次性混合紋理。然後,當需要從一個動畫循環切換到另一個動畫循環時,我們在運行時讀取這個預烘焙的紋理。

這種方法的主要問題是它沒有很好地擴展。事實上,它在存儲佔用空間方面呈指數級增長,並且使用我們的19個動畫循環,必須編寫462個過渡紋理,總計不到60Mb的數據。

我們可以通過在運行時將動畫數據烘焙到動態渲染器目標來緩解這種情況。這將使我們能夠在運行時執行更復雜的動畫藍圖驅動的混合邏輯,而無需一組混合紋理,但會以犧牲一些運行時性能爲代價。

擴展系統的另一種方法是通過將變形目標編碼爲幾何圖形的頂點顏色和/或法線來爲每個網格實例引入更多變化,類似於"靜態網格體變形目標"系統的工作方式。

總之,我們能夠創建廣泛重用動畫數據的系統,這些數據可以很好地擴展並以較低的,基本上固定的性能開銷運行。


原文鏈接:20萬動畫角色UE4優化實戰 — BimAnt

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章