動畫重定向技術分析及其在Unity中的應用

原文鏈接:動畫重定向技術分析及其在Unity中的應用 - Blog

這是侑虎科技第228篇原創文章,感謝作者賈偉昊供稿,歡迎轉發分享,未經作者授權請勿轉載。當然,如果您有任何獨到的見解或者發現也歡迎聯繫我們,一起探討。(QQ群:465082844)

作者簡書:董夕 - 簡書

前言

筆者新的手游項目使用Unity引擎,動畫部分要使用重定向技術來實現動畫復用。筆者之前在大公司工作的時候對這塊了解比較深入,讀過Havok引擎在這部分的實現源碼,並基於自己的理解,在公司自研的手游引擎中實現了一個簡化的版本。本文將從原理層面分析Animation Retargeting技術的實現方式,然後落回到Unity引擎中,記錄筆者使用Retargeting時遇到的問題和解決方法。

壹 · 什麼是動畫重定向

動畫重定向,即Animation Retargeting,是一種動畫復用的技術。直觀上,你可以把它的作用想像成周星馳電影《西遊-降魔篇》里的「聽話符」——「我做什麼,你就跟我做什麼」。

聽話符

這裡給出一個非官方的定義:

Retargeting is just the process of 「copying」 the animation from one skeleton to the other.

簡單來說,重定向技術就是一種將動畫數據從一個骨架拷貝到另外一個骨架的過程,只是這個所謂的「拷貝」過程,沒有Ctrl+C、Ctrl+V這麼簡單。其實現的方式,也會影響到動畫應用到另外一個骨架之後的效果。

貳 · 為什麼要使用動畫重定向

動畫重定向技術在很多地方已經得到很廣泛的應用,比如目前3A級主機遊戲常用的動作捕捉技術就是基於這個原理來實現的——將真實人物的動作通過圖像識別等技術生成動畫信息應用到虛擬的角色身上,保存成動畫數據。還有Max和Maya這樣的建模工具中,也都已經集成了這一技術,用來方便美術把一個動作文件復用到其他骨架上。

那什麼類型的遊戲開發需要這樣的技術呢?我們可以假想,如果要開發類似《街霸》這樣的3D格鬥遊戲,其中有幾十個不同體型、姿態各異的角色,戰鬥過程中會有很多抓技和投技的動作表現,對於這一類型的技能,不同角色的受擊動作應該是一致的。

如果使用最常規的製作方法,需要對每一個角色製作攻擊動作對應的受擊動畫,或者需要限制策劃設計的受擊表現在使用固定姿勢、掛接、特效等方式可以實現的範圍內。而不甘平庸的策劃同學往往需要更加複雜的受擊表現,常規的製作方法帶來的問題可能有如下三種:

  1. 美術工作量較大。 如果有n個不同角色的骨架,有m個需要特殊表現的技能,那麼只考慮受擊動畫就需要m*n個,這個數量級通常會在幾千個動作的級別。美術需要製作、導出這些動畫文件,需要非常大的工作量。

  2. 動畫維護困難。 想像下,策劃設計了一個需要特殊動作表現的技能,美術加班加點製作出了所有英雄的受擊動畫,第二天黑著眼圈來上班,策劃同學滿臉訕笑地走過來說——「哎呀,你看,這個受擊動作的第15幀有點不自然,力量感不夠強,要你不優化一下?」……每一處修改都需要美術手動同步到其他動作文件上,重新調整導出,這一過程太費時費力。

  3. 遊戲發布打包文件過大。 動畫文件多必然帶來這一問題,尤其是在手游中,包體大小通常是安卓渠道發行的一個需要考慮的重要指標。

動畫重定向技術就是一種時間換空間的思路,在運行時將一套動畫根據預計算好的骨骼差異信息,得到目標模型骨架上可以適用的動畫數據,就可以生成目標角色的受擊動畫效果。這種方式既可以減少美術的工作量,又可以增加遊戲對策劃需求和修改的靈活度,將需要維護和打包的動畫數量從m*n這樣的平方級別降低到m個。當然,軟體工程中「沒有銀彈」的定律在這裡依舊生效,重定向之後的動畫可能由於體型差異等問題在某些情況下無法完全滿足策劃或者美術的要求,這就需要一些額外的方法或者重新製作部分動畫來進行彌補,這部分放在後面進行詳述。

叄 · 基本原理

寫到這裡,其實之前有一點沒有說明的是動畫重定向技術主要是針對骨骼動畫的方案,由骨骼來描述動作信息,用蒙皮來表示模型網格與骨骼之間的關係,從而得到模型最終的樣子。對於這塊不熟悉的讀者可以自己查閱一下相關的資料。

骨骼動畫

動畫重定向的過程主要是針對骨骼信息,對於蒙皮過程沒有任何影響。而動畫信息,可以理解為每一幀中所有骨骼數據信息的集合。我們可以這樣更加形象地理解:動畫就是每一幀為模型製作一個Pose(姿勢),在每幀之間的姿勢可以通過差值獲得。

為了更加清楚地描述動畫重定向地原理,我們截取某一幀的姿勢來進行分析——在解決了每一幀的姿勢重定向之後,整個動畫的重定向,也就是在每一幀都進行姿勢的重定向即可。

假設有兩個模型,它們的骨架分別是A和B,我們擁有A骨架對應的動畫數據,想把這個動畫數據應用在B骨架上。直接應用是否可以?當A骨架和B骨架完全一樣的時候,直接應用是可以的,但通常動畫重定向要處理的是骨架不太一樣的情況。這些不一樣的情況有很多種,比如:

a) 骨骼數量不一致;

b) 骨骼父子關係不一致;

c) 骨骼名稱不一致;

d) 骨骼本身的長度等數據不一致。

對於前幾種不一致情況的處理,我們在基本原理的部分先不進行分析,對於骨骼數量一致、骨骼父子關係也相同這樣最為簡單的情況,我們來看一下動畫重定向的基本方式。

先明確在這種情況下,直接應用A的動畫數據到B上會存在的問題。想像一下,A是一個身高為1.8m左右的大人骨架,而B是一個身高為1.5m左右小孩骨架,A的動畫信息記錄的骨骼位置(Position)、旋轉(Rotation)和縮放(Scale)信息都是針對於大人的,比如經過最終的計算,在Idle動畫中的某一幀姿勢中,模型空間下A的Head這根骨骼可能在距離地面1.7m這樣的位置,而同樣的動作讓一個小孩B來做,也把Head骨骼放置在1.7m的位置,蒙皮之後的結果會非常奇怪,出現拉伸等問題,因為B的身高整個才是1.5m。

為了解決這個問題,或者說一個正確的動畫重定向過程,是基於所謂的參考姿勢(Reference Pose)的。通常情況下,會把T-Pose作為參考姿勢。引入參考姿勢之後,動畫數據不再直接應用到目標骨架上,而是把動畫姿勢與參考姿勢的差異應用到目標骨架上。a1是A骨架的參考姿勢,b1是B骨架的參考姿勢,動畫中某一幀的姿勢是a2,我們想得到的結果是b2,我們認為,a2與參考姿勢a1的差異應當和b2與其對應的參考姿勢b1的差異相同,即:

a2 - a1 = b2 - b1

我們可以得到計算過程為:

b2 = a2 - a1 + b2 = a2 + (b1 - a1)

這其中,a1和b1的數據是在遊戲發布時就確定的,因此可以進行預先計算好b1-a1的值。要知道這裡的加法和減法要轉換為每根骨骼的PRS計算,因此還是有不少CPU消耗的。下圖給出了使用一個簡單整數代替骨骼的PRS數據來模擬動畫重定向的計算過程。

簡化版本的動畫重定向原理

從上圖可以看出,在Setup階段,可以計算出b1-a1的值,然後運行時只需要根據動畫數據這個diff值就可以得到最終想要的動畫效果。這裡需要說明的是,在Havok引擎的實現中,會根據a1和b1兩個參考姿勢中每根骨骼相對於父骨骼的Position計算出骨骼的長度,比如a1中Head骨骼的長度為1.0,b1中Head骨骼的長度為1.5,計算diff的結果時會把這兩個值的比例應用到scale部分,即會給scale * 1.5,來保證最終映射出來骨骼位置的合理性。這部分的實現太過細節,其實只需要做這部分實現的人理解即可,有興趣的朋友可以留言討論。

在引擎實現方面,Havok引擎使用了單獨的mapping文件來存儲b1-a1的數據,並且這部分數據具有一定的可定製性;在我自己後來的實現中,採用的方式是載入模型文件時檢查該模型是否需要動畫重定向,如果需要則計算一份對應的diff信息放在內存中。兩種實現方案的區別不大,只是時間和空間的互換,後者是在需求比較確定,減少美術工作量的考量下選擇的實現方式。

總之,基於參考姿勢的動畫重定向計算,不但可以保證大部分情況下可以有良好的動畫效果,而且可以保留目標骨架的一些基本姿勢特徵,比如駝背、八字腳等,應用得到的話是可以讓重定向之後的動畫也有這些特種。

肆 · 骨骼映射

前面的基本原理只解決了骨骼長度等數據不一致的情況下的動畫重定向問題,在骨骼數量、名稱甚至父子關係等都不一致的情況下,該如何處理呢?

對於手游應用者來說,答案很簡單——盡量不要出現上述的這些情況。雖然有各種技術方案可以一定程度上處理和解決上述問題,但目前工程方面並沒有非常完美的效果,而且可能在性能、製作流程複雜度上導致一些問題。

應用動畫重定向技術的原則應當是——在項目初期做好技術預研、確定使用方法、制定從骨骼結構到骨骼名稱等相關的美術規範。在中後期引入這一技術的應用,在美術製作不規範的情況下,就有可能帶來較大的額外工作。

這裡,針對不同的問題,給出一些解決方案的思路,某些技術細節的實現就不進行詳細的說明了。

4.1 骨骼名稱不一致

在項目開發中,出於進度的考慮,往往會把不同角色模型和動畫的製作外包給不同的外包商,他們對於骨骼的命名可能會不同,比如常見的Biped Head和Bip001 Head。對於CS骨骼,由於在3DS Max中通常美術只會添加不同的前綴,因此可以通過去除前綴的方式進行模糊匹配來做骨骼映射;Unity的做法細節不清楚,但是筆者感覺會根據整個骨架的父子關係和結構來進行映射關係的計算;而對於Bone骨骼,在沒有預先定義好類似最大化骨骼這樣規範的情況下,非常難通過程序來判斷映射關係,可以提供可視化編輯的功能來讓美術自己定義它們之間的映射關係。

4.2 骨骼數量不一致

我們之前討論的映射關係,都是一根骨骼對應一根骨骼的簡單映射方式。在骨骼數量不一致時又可以分為兩種情況來討論:

1)在非CS骨骼或者不重要的部分存在多餘的骨骼。如果動畫文件的骨架中存在多餘骨骼,通常的做法是把這些骨骼忽略掉,而如果目標骨架上存在多餘的骨骼,即有些骨骼原始動畫中並不存在,這其實沒有辦法為它生成動畫,只需要保證其保留在原始姿勢的local space當中,即讓其跟著父骨骼移動。比如身上的飄帶,如果原始動畫中沒有,在不使用布料系統等物理方案的情況下,只能讓其按照參考姿勢中的樣子,「僵硬」地跟隨角色移動。

2)在重要的位置存在骨骼不一致。一個常見的例子是胸部的脊柱,不同的角色可能脊柱骨骼數量不同,從2根到4、5根都很常見。這種情況下,簡單的一根骨骼映射到一根骨骼就無法做到很好的效果,如下圖所示(使用Havok文檔中的例子):

鏈式映射 1

我們想讓藍色小人變到黃色小人當前的姿勢,藍色小人胸部只有一根骨骼,而黃色小人有5根,那麼讓這一根骨骼使用黃色小人那5根骨骼中的任何一根結果都不理想,可能會出現如下圖所示的這種結果:

鏈式映射 2

這種結果最後渲染出來可能是一個頭向前傾斜,但是背依然挺直的角色,它脖子處的蒙皮被拉得很長。這不是我們想要的效果,如果使用鏈式映射,可以做到如下圖所示的這種較好的結果:

鏈式映射 3

注意,這裡同時修復了頭頂上的兩根骨骼沒有跟隨父骨骼移動的問題。

鏈式映射要做的就是將多根骨骼組成的骨鏈A和另外一個骨骼中多根骨骼組成的骨鏈B進行映射,做到整條B骨鏈的樣子和A骨鏈的樣子相近。

那麼鏈式映射的基本原理就是:首先對齊起始鏈骨骼,然後再保持骨鏈原有樣子的條件下,盡量對齊它們的結束骨骼。下圖給出了一個簡單的示例:

鏈式映射原理 1

上圖把兩根骨骼形成的骨骼鏈接映射到四根骨骼形成的姿勢上面。

鏈式映射原理 2

上圖把四根骨骼形成的骨骼鏈接映射到兩根骨骼形成的姿勢上面。

那麼如何對齊結束骨骼呢?這裡只簡單描述Havok引擎中的計算方法,首先算出從骨鏈的開始骨骼到結束骨骼的向量,比如A1,B1,然後將這兩個向量規範化,計算可以將B1轉到A1最小旋轉,這個旋轉使用四元數表示,描述了一個和兩個向量都垂直的軸外加一個旋轉角度Theta值。這個四元數的計算過程可以使用向量的點積、叉積和半形正玄公式、二倍角正玄公式得到,避免了三角函數計算這種很耗的計算過程。下圖給出了一個簡單的計算過程,可以看出其使用向量計算來進行效率上的優化。

簡化的旋轉計算過程

4.3 骨骼父子關係不一致

當兩套骨骼的父子關係都不一致的情況下,其實很難得到正確的映射,簡單的不一致可能可以容忍,但是可以想像,把一個人形骨骼的動畫映射給四足動物甚至蜘蛛這樣的八腳動物,是一件非常難做的事情。

也因為這樣的原因,目前大範圍應用的動畫重定向,基本還是在人形骨骼上,當然,用相同的演算法,把四足的戰馬動畫映射到不同的體型的戰馬上也是可以的。基本的原則是骨架盡量具有更多的相似性,重定向的效果也就會更好。

伍 · Unity引擎中的應用

在了解了基本原理之後,我們來看一下動畫重定向技術在工程中的應用。筆者之前使用Havok引擎和實現自研引擎中的相應功能有較多的應用經驗,但是Unity引擎還是摸索了一下才找到正確的使用方法,官方文檔(Retargeting of Humanoid animations)給出了一個簡單的應用過程和效果截圖,但這個文檔講述的過程比較簡單,一些注意事項也沒有說得很清楚。

5.1 基本使用方法

首先,Unity引擎中動畫重定向的實現不是一個直觀的方法,而是封裝在了Humanoid類型的動畫系統裡面,也就是必須是人形的骨架、使用Humanoid才可以使用它。Unity沒有像前文描述的基本原理那樣去定義兩套骨架之間的映射關係,而是自己在內部定義一套骨架模板,所有的Avatar骨骼都必須映射到這套模板上才可以由同一個Animator來驅動產生Retargeting之後的動畫效果。

Unity中的骨骼映射設置界面

在導入一個模型文件之後,要在這裡設置每根骨骼的映射關係,這個映射關係默認會自動建立一套,可以根據需要進行調整,也可以保存和導入配置好的映射關係。

關於如何預覽一個重定向之後的動畫的效果,筆者還沒找到非常方便的辦法,只能把模型放到Scene中,設置同一個Animator來觀察,在動畫文件的預覽窗口,如果拖拽另外一個模型文件到其中,並不能預覽到正確的效果,這跟我之前預想的不太一樣。這裡簡單說明一下整個設置過程,假設我們想讓一個female的模型復用一個male的動畫:

  1. 導入male的模型,設置動畫類型為Humanoid,設置好Avatar上的骨骼映射關係;
  2. 導入male的動畫文件,設置其類型為Humanoid,講其Avatar設置為male的avatar;
  3. 使用male的動畫製作一個Animator;
  4. 導入female的模型文件,設置動畫類型為Humanoid,同樣設置好Avatar上的骨骼映射關係;
  5. 可以拖拽一個模型文件到Scene中,然後把它的Animator設置為之前male的動畫製作的Animator,即可看到Retargeting之後的效果了。

這一過程比較簡單,Unity引擎為用戶隱藏了非常多的實現細節,讓用戶在進行盡量少的配置的情況下使用動畫重定向功能。

5.2 遇到的問題記錄

在使用過程中,我們遇到了一些問題。

  1. 角色的武器或者飄帶在使用Humanoid類型的動畫系統之後不會移動了,或者移動的位置有了很大的偏差。這時候可以在動畫文件的屬性設置里,查看Mask下的Transform選項,裡面可能存在沒有被勾選的骨骼。

Unity動畫設置

我們目前的做法是把Transform下的所有骨骼對象都勾選上。

2)角色的武器在動作中出現了亂飄的情況,與手部無法緊密地綁定在一起。這是由於我們最初為了方便美術製作武器的動作,把其父骨骼設置給了盆骨這樣一根相對穩定的骨骼,但是經過Retargeting計算之後,由於角色身材不同產生了一些偏差導致。最終我們還是把武器骨骼的父骨骼設置為手部的骨骼,才解決了這一問題。

3)某些角色在重定向之後的動畫中表現為腳不貼地,和地面之間有縫隙,原始動畫中沒有。如果你理解了動畫重定向的原理,那麼當原始動畫沒有問題、而重定向之後的動畫存在問題時,可能是重定向演算法或者是參考姿勢這兩個因素導致。我們在沒有代碼授權的前提下無法查看和修改Unity引擎的重定向實現代碼,而且Retargeting功能已經發布了這麼久,理論上大部分的Bug都應該已經修復了...在Avatar的Configuration界面中,除了設置骨骼的映射關係之外,還可以設置T-Pos的姿勢,這裡我們可以對參考姿勢做微調,如下圖所示:

Unity中調整參考姿勢

注意,Unity定義了一個基本的T-Pos姿勢範圍,當調整的結果超出範圍的時候,會出現紅色的提示字樣。我們的問題是目標角色的腳步骨骼在參考姿勢中沒有貼地,導致了重定向之後的問題。當你發現重定向之後的動畫有些骨骼的姿勢很奇怪,或者想讓你的目標角色有一些自己的特有個性(比如外八字腳之類)的時候,可以在這個界面進行調整。

5.3 性能消耗

通過前面的原理分析可以看出,即使在有預計算的情況下,與普通的動畫計算,Retargeting的過程還是有一定的CPU消耗的,但是這與通常會造成CPU瓶頸的蒙皮、渲染指令提交等相比,其實消耗並不算大。由於Unity與Retargeting相關的還製作了肌肉控制的功能,我擔心這部分會有額外的性能消耗,還專門拜託朋友幫忙詢問了Unity大中華區的技術人員,得到的答覆是——Humanoid形式的動畫系統相對於Generic形式的動畫系統雖然有一部分額外的性能消耗,但是Unity內部做了比較好的優化,差別不是很大,因此可以放心使用。我們由於必須使用Retargeting的功能,因此也沒有對幾套動畫系統進行非常詳盡的性能對照測試,但從以往遊戲開發的經驗來看,動畫重定向這塊通常不會在後期的性能Profile過程中被揪出來要求優化。

總之,雖然動畫重定向這套東西已經很熟悉了,但是在Unity中的應用,我們還是摸索的新兵,目前也只是項目的初期,遇到的問題不是很多,如果有朋友有相關的經驗可以介紹和分享,還望不吝賜教。

陸 · 其他

如果你去搜Animation Retargeting相關的論文,會發現學術界有很多更加深入的研究,但是很多演算法都不適用於當前的遊戲開發。而對於差別非常大的骨架,其實也沒有必要非要去用重定向的技術,可能美術單獨製作動畫資源效果會更好。

在之前搜集資料的過程中,讀到一篇關於《spore》遊戲的論文,覺得比較有意思,《Real-time Motion Retargeting to Highly Varied User-Created Morphologies》。有興趣的朋友可以去讀一下,《孢子》這款遊戲使用的實時動畫系統中就有Animation Retargeting技術的應用,結合IK(反向運動學)技術,實現了遊戲中自定製角色的動畫表現,效果非常不錯,玩過的朋友可能會有印象。

柒 · 結語

動畫重定向系統就像很多的遊戲引擎組件一樣,是一個可能從外部看上去似乎很深奧複雜的功能,但是深入其實現原理時,卻發現其實非常簡單。然而,要把這樣一個功能封裝成一個易用性很強的引擎模塊,讓用戶可以在不理解原理的情況下去輕鬆地使用它又需要解決很多實際問題,開發各種編輯工具。把一項技術,應用到實際的遊戲開發中,就需要這樣一個由難入易,再由易到難的過程,這也是做科學研究與做實際工程的區別之一吧。

希望本文可以給需要用到動畫重定向技術的朋友以幫助,也歡迎更多的朋友進行討論和分享。對於遊戲開發這樣一項工作來說,永遠都有學習不完的知識和技術,而技術的積累和沉澱,是把遊戲做得更好的有力保障。

文末,再次感謝賈偉昊的分享,如果您有任何獨到的見解或者發現也歡迎聯繫我們,一起探討。(QQ群:465082844)。

也歡迎大家來積极參与U Sparkle開發者計劃,簡稱"US",代表你和我,代表UWA和開發者在一起!


推薦閱讀:

如何評價《全職高手》的動畫pv?
近期看的動畫 (1)
如何評價《愛寵大機密》里的兔子?
追番看三話,四月新番月季掃雷(上)

TAG:Unity游戏引擎 | 动画 | 骨骼 |