骨骼動畫的原理及在Unity中的使用


制作骨骼動畫


我們看看這幾步操作后,我們得到了那些數據:
1.每個皮膚頂點的初始世界坐標。
2.每個骨骼關節頂點的初始世界坐標。
3.每個頂點被骨骼頂點的影響信息。
4.骨骼如何移動。

骨骼動畫原理

核心: 通過骨骼帶動皮膚運動,也就是通過骨骼的移動動態計算mesh上的點的位置

過程:

1.將mesh上的點轉換為骨骼空間上的點。
骨骼空間就是以關節為原點確定的空間,並不是一個實體。
2.通過縮放、旋轉、平移將骨骼移動到新的位置。
3.根據骨骼的新位置計算mesh頂點新世界坐標(骨骼移動,但mesh頂點與骨骼的相對位置不變,所以產生了頂點隨骨骼移動的感覺),若一個頂點被多個骨骼影響,則要進行頂點混合計算新世界坐標。

舉例

初始位置(綁定姿勢):

骨骼移動后的位置:

1.計算小臂上一點S在小臂空間中的位置。
這個就要根據初始的骨骼位置和mesh上頂點的位置來計算了,也就是常說的綁定姿勢狀態。
先說一下該例中每個坐標的意義:
(x1,y1,z1):左肩關節SL的世界坐標。
(x2,y2,z2):左肘關節在以左肩關節為原點的坐標系本地坐標。
(x3,y3,z3):附着於左小臂上的皮膚上的一點S的世界坐標。
這里為了簡單,假設所有的關節都沒有經過旋轉和縮放。
先將S轉換到通過SL確定的空間中,也就是大臂UAL空間,直接減去SL的坐標即可(x3-x1,y3-y1,z3-z1)。
再將S點在大臂UAL空間中的坐標轉換到小臂LA空間中,直接減去EL的坐標即可(x3-x1-x2,y3-y1-y2,z3-z1-z2)。

以上只是一個簡單的說明,而實際的使用中,初始的骨骼位置可能是通過縮放、旋轉、平移后得到的,一般會通過矩陣的方式來表示這一系列變換。
關於OpenGL中的坐標轉換可以參考這個鏈接的說明LearnOpenGL CN
看完這個我們就應該知道如何將一個子坐標空間的點轉化為世界坐標了。下面再使用該例子進行舉例。
先計算模型矩陣再求模型矩陣的逆矩陣:

或者直接將求模型矩陣的運算反過來也可以:

得到World→EL矩陣后就可應直接通過矩陣運算直接將世界坐標上的一點轉化為EL空間上的一點了。

注意:
1.注意矩陣的運算順序,因為矩陣運算是不滿足交換率的,如果順序錯了,結果很可能也就錯了。
2.矩陣EL→World一般叫做EL空間的模型(model)矩陣,矩陣World→EL一般叫做EL空間的綁定姿勢矩陣(bindpose)
3.有時存儲的Mesh頂點信息不是直接的世界坐標,而是一個有層次結構的mesh,但這並不影響流程,只要在運算時增加一步將這些頂點轉化為世界坐標的操作即可。
2.計算EL順時針旋轉90°后S點的位置。
直接通過左肩SL,左肘關節EL的縮放、旋轉、平移信息計算小臂LA空間的模型矩陣,使用上一步算出的小臂LA空間坐標乘以該模型矩陣即算出了該點收到骨骼移動的影響后的位置。

3.頂點混合
有一些頂點不一定只受一個骨骼的影響,可能受多個骨骼的影響,此時就要通過頂點混合計算該點的新坐標。
現在假設點S同時受SL,EL的影響且影響權重分別為0.4,0.6。
①分別計算點S在SL、EL空間中的本地坐標。

②分別計算點S在SL、EL移動后的世界坐標。

③根據SL、EL對點S的影響權重混合坐標,獲得新的世界坐標。

這里舉例的是受兩個骨骼影響的情況,受3個、4個時原理也是相同的,只不過運算量會更大一些。

小結

1.空間的平移、旋轉、縮放都可以用矩陣來表示,而且這些矩陣也可以結合在一起成為一個矩陣,多層空間結構的變換也一樣可以組合為一個矩陣。
2.現在看來骨骼動畫的核心其實就是幾個矩陣乘法的問題,大概就是這樣:

其中model矩陣隨着動畫的播放不停的變化,也就實現了骨骼帶動皮膚的功能。
假設一個頂點受多個骨骼影響,那么就再根據權重混合一下。
3.mesh的初始位置、bindpose、影響因素都是通過制作該骨骼動畫模型時確定的,可以參考第一小節制作骨骼動畫的過程。

Unity中的骨骼動畫

我們這里以一個Mixamo上的免費資源Samba Dancing為例。

資源下載


資源導入

直接將資源拖入Unity中即可,可以看到在Unity中生成了一個文件夾和一個預制件。

加入動畫

1.把模型prefab拖入場景中。
2.然后將mixamo.com動畫拖到場景中的Samba Dancing中,Unity會自動生成對應的Animator Controller。

運行場景,查看動畫效果

直接點擊運行即可。

數據說明

動畫數據說明:


左邊是每一幀變化的骨骼,右邊是每個骨骼關鍵幀的平移,旋轉,縮放信息。

模型信息說明

Skinned Mesh Renderer屬性詳解

Cast Shadows:是否投射陰影。
Receive Shadows:是否接收陰影。
Materials:材質。
Use Light Probes:是否使用光探針。
Reflection Probes:反射探針設置。
Anchor Override:網格錨點。
Lightmap Parameters:光照烘培參數。
Quality:每個頂點最多收到的骨骼影響數量。
Update When Offscreen:當mesh在屏幕外時是否更新,依據RootBone和Bounds判斷。
Mesh:Mesh信息。
mesh信息包含了每個頂點的位置信息,受骨骼影響的權重信息、切線、法線、UV映射信息。
RootBone:根骨骼,有兩個作用。
1.作為mesh在屏幕外時是否更新的依據。
2.進行坐標計算時的Root空間。
在Unity中計算mesh上一點位置的流程大概是這樣的:

先通過上述一系列計算得到點在RootBone空間中的位置,上述過程對開發者時不可見的。然后將接下來的步驟交給Material中的Shader解決。查看Shader文件可以看到,在頂點着色其中第一步會給輸入的點乘以一個MVP矩陣獲取該點在屏幕上的位置,其中的M就是RootBone的模型矩陣。
以Unity 5.37的Standard Shader為例,截取其使用的頂點着色器的一部分

VertexOutputForwardBase vertForwardBase (VertexInput v)
{
    UNITY_SETUP_INSTANCE_ID(v);
    VertexOutputForwardBase o;
    UNITY_INITIALIZE_OUTPUT(VertexOutputForwardBase, o);
    UNITY_TRANSFER_INSTANCE_ID(v, o);
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);

    float4 posWorld = mul(unity_ObjectToWorld, v.vertex);
    #if UNITY_REQUIRE_FRAG_WORLDPOS
        #if UNITY_PACK_WORLDPOS_WITH_TANGENT
            o.tangentToWorldAndPackedData[0].w = posWorld.x;
            o.tangentToWorldAndPackedData[1].w = posWorld.y;
            o.tangentToWorldAndPackedData[2].w = posWorld.z;
        #else
            o.posWorld = posWorld.xyz;
        #endif
    #endif

可以看到,通過posWorld = mul(unity_ObjectToWorld, v.vertex);對頂點以RootBone空間為基礎做了轉換。

Bounds:根骨骼的邊界。

如果有什么錯誤,希望各位在博客下留言指正,我會盡快改正。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM