
綁定是實作骨骼動畫非常重要的一個技術。尤其在人物,生物上非常常用。隻要模型上綁定好骨骼,就能通過一套預置好的動畫資料來實作一些諸如行走,奔跑等動作。
要在模型上綁定骨骼。一般是在 maya,blender 一類的 3d 軟體中實作的。幾乎很少(通常也沒必要)在遊戲引擎中去做實時綁定。一是耗時長,模型越複雜,頂點需要計算的數量就越多。二是大多數美術資源都是預制好的,從模型,貼圖到綁定蒙皮。
個人一直關注 procedural generation。經過實踐,在遊戲引擎内已經可以做到模型和貼圖的實時生成。如若能把綁定環節也解決,就能涵蓋更完整的流程。
這是近期一個探索成果。整個視訊的生物都是實時用算法生成的。優點是形态多樣,不占用儲存空間。傳統的制作流程上,若要出現1萬種生物,就需要1萬個模型。會産生非常可怕的資料量。雖然能通過提取子產品,在一定程度上進行壓縮,但靈活性相當有限。
上面視訊中的生物模型生成用到了 marching cube + signed distance functions。這裡重點會分享關于實時綁定的實作思路以及解決方法。
解決過程
若直接在google 搜尋 procedrual rigging 一類的關鍵詞,幾乎沒有相關教程。(可見這一技術并不常用)而解決這一問題的突破口,是從一個叫 tail animator 插件中獲得啟發的。該插件提供了一個能在引擎編輯器内綁定指定模型的功能。經過簡單的改寫,讓它支援能在 runtime 中實時綁定,也實作了 Test 403 的版本。
但插件中的綁定性能相當不理想,上述的 lowPoly 風格的模型,僅僅是幾百幾千的頂點,生成時就能發現可察覺的延遲。是以當時的想法,就是盡量了解内部源碼,并設法在 compute shader 上實作,來加速綁定過程
(Tail Animator 中的部分代碼)
經觀察發現。略去具體的實作細節,和綁定相關,需要進行計算與指派操作的相關變量如下
mesh.boneWeights = weights; // BoneWeight[]
mesh.bindposes = bindPoses; // Matrix4x4[]
rend.bones = bones; // Transform[]
rend.sharedMesh = mesh;
按圖索骥,從Unity的文檔中找到了一段關于Mesh.bindposes的範例
(https://docs.unity3d.com/ScriptReference/Mesh-bindposes.html),
其中建立了一個 quad ,以及兩個 bone 來進行綁定。并且生成了一個 AnimationClip 來驅動骨骼進行移動。運作效果如圖。
* 若關閉掉 AnimationClip ,就能手動移動骨骼頂點,去影響這一 quad 面片
* 讓人欣喜的,這段代碼是一個最小可用範例,展現了清晰的結構。經過閱讀,裡面比較陌生的資料類型是 BoneWeight ,檢視後結構如下
(官方一個比較清晰的解釋:https://docs.unity3d.com/ScriptReference/BoneWeight.html)
Skinning bone weights of a vertex in the mesh. Each vertex is skinned with up to four bones. All weights should sum up to one. Weights and bone indices should be defined in the order of decreasing weight. If a vertex is affected by less than four bones, the remaining weights should be zeroes.
根據這一資訊,可以看到每個頂點最多可以受四個骨骼節點影響。我們可以人為地配置設定權重來決定哪個節點對頂點的偏移影響最大。若了解沒有錯誤,你在引擎内看到的所有細膩的綁定效果,都是基于這一規則去實作的 ,非常巧妙。
例子中 weight 的數量為 4。bone ,bindPoses 的數量為 2。其中 weight 的數量對應的就是頂點數。而 boneIndex 的值表示每個頂點應該綁定哪個骨骼。weight0 設為 1 則表示該頂點隻受一個骨骼影響
而表示骨骼的 Transform 數組由于是元件,是以要先建立新的 GameObject 來挂載。同時将它變成渲染物體的子對象。位置數值按需設定,旋轉數值設為 Quaternion.identity ,表示“沒有旋轉“。作為預設值。而 bindPoses 本身是一個矩陣,具體含義可參考
(https://forum.unity.com/threads/some-explanations-on-bindposes.86185/)
(https://gist.github.com/larryhou/5ccb8cec465024fe3239ddd4ffce44e5),
初始化時隻需按如下格式操作即可
基于這個了解,後面也先後建立了兩個版本(Cpu/Gpu)去針對特定模型自動綁定骨骼。而 GPU 版。經過粗略測試,上萬的頂點綁定連同模型的生成耗時不超過30ms,效率驚人。也讓實時綁定的技術真正有了生産力。
下圖是運作後的結果,以綁定一個觸手為例。完整的代碼已上傳Github
(https://github.com/Wenzy--/Procedural-Rigging-in-Unity)
具體細節不在此展開。有三個要點需要注意。
* 骨骼在組織時,一般要有父子層級的嵌套關系。這樣父層級的骨骼改變,也能影響所有子層級的。(例如肩關節旋轉或位移,手肘,手掌,手指等各節點也會跟随)
* 計算權重時,會基于mesh 頂點到骨骼的距離做計算。但由于骨骼的坐标與 mesh 的頂點資料不在同一參考系,計算前需要先做轉化
* 計算權重的一個原則,距離頂點越近的骨骼,配置設定到的權重越高,也越易影響模型
下階段展望
有了目前的基礎,下一步計劃就是進行人物的實時綁定,有感興趣的朋友歡迎交流探讨。很久前有關注 @_alexeykalinin 的作品,他也完成了一套程式化綁定的工作流。但由于使用的工具是 Houdini,是以也隻能在編輯器中生成。而無法做到實時。但可以預見,實時綁定+實時模型生成在圖形創作上有巨大的潛力
(https://twitter.com/i/status/1205907693543788549)
(來自alexeykalinin)