1. 程式人生 > >骨骼動畫驅動的一般方法,以及使用幸運飛艇平臺出租FBX SDK的一些實現

骨骼動畫驅動的一般方法,以及使用幸運飛艇平臺出租FBX SDK的一些實現

我終於要更新幸運飛艇平臺出租Q1157880099部落格了,進公司之後每天學的做的很多,但真正有時間去總結的時候很少。之後的話儘量每週都要做一個知識的總結和積累吧,一方面自己真的是做過就忘,另一方面也能夠將自己踩到的坑,遇到的問題與大家分享一下。

入職三個月吧第一個月最主要的事情就是在骨骼動畫的驅動,一開始是研究的Assimp這個庫,也實現了相應的功能,但後來發現坑實在是太多,對之後的擴充套件也不好,最後還是開始啃超級不友好的FBX SDK。也算是把整個過程理順了。Assimp的先留個坑,之後再總結。

萬萬沒想到是我還是做起了OpenGL,比之前OSG感覺還要難一些,不過shader好有趣啊,現在終於會寫shader,之前都覺得在看天書,Unity也在繼續做,目前主要就是在Unity上實現demo,然後移植到OpenGL中。現在的工作真的很讓人進步,也很開心~加油吧~

————————————————————————————————————————————————————

骨骼動畫驅動的一般方法

(一)  所需資料

1、  One or multiple Mesh:包含在世界座標下的(fbx中其實是相對於所輸mesh對應的node座標下的),in bind pose的頂點座標

2、  A hierarchy of bones:骨骼層次,有些地方也叫frames,參考座標系,實際上每個骨骼就是一個座標系。除了根骨骼以外,所有骨骼都有一個parent bone,none or multiple children bone。

3、  An array of matrices:每個骨骼都有一個matrix,通常叫nodeTransform。是父骨骼空間到該骨骼的local space的transform ,in bind pose。相對於父節點

的。local

4、  An array of matrices: 每個骨骼都有一個matrix,通常叫bind pose matrix,offset matrix etc. 每個matrix 是從world coordinate到該骨骼的local space的transform。

5、  A collection of animation data: 通常是KeyFrames。儲存的資料是一組對每個骨骼的關鍵幀資料,以及關鍵幀時間。這個transform通常是local的。

計算bind pose matrice的方法:

bone’s bind pose matrix = inverse(bone’slocal matrix(nodeTransform)* parent’s local matrix * parent’s parent’s localmatrix*….);

(二)  Principle

1、  計算一個animationmatrix for each bone,是一個local transform from the parent’s bone space to the bone’s animatedorientation.

2、  combine matrix, 將每個骨骼的animation matrix與父骨骼的animation matrix相乘,級聯。遞迴。得到combine matrix,是從該骨骼的local space 到 world space的transform。

3、  final matrix = bind-pose-matrix * combine matrix (乘法順序由API決定,OpenGL是右乘)

4、  在vertexshader中,每個頂點都會乘上影響它位置的骨骼的weighted 的final matrix。

(三)  For FBX SDK

對於FBX SDK來解析模型的mesh和頂點等還是很簡單的,比較容易困擾的就是骨骼動畫驅動中會用到的這些矩陣。所以也主要介紹一下這一部分,有時間的時候會將整個流程都記錄下來。我們的需求是將模型檔案解析出來,之後用OpenGL來渲染,在渲染過程中不想使用到Fbx SDK的方法等,所以都是把相關的資料儲存成自己的類。

1、nodeTransform:node->EvaluateLocalTransform();

返回bind pose下該節點的local transformation matrix. 該transform有考慮到pre/postrotation之類的變換

函式原型及需要注意的點:

FbxAMatrix&EvaluateLocalTransform(FbxTimepTime=FBXSDK_TIME_INFINITE, FbxNode::EPivotSetpPivotSet=FbxNode::eSourcePivot, boolpApplyTarget=false, boolpForceEval=false);

remarks The local transform matrix iscalculated in this way: ParentGlobal.Inverse * Global, all transforms such as pre/post rotation are takeninto consideration.

This will return a different valuethan LclTranslation, LclRotation and LclScaling at the specified time. Toevaluate these properties separately

* without taking pre/post rotation,pivots and offsets into consideration, please use GetNodeLocalTranslation(),GetNodeLocalRotation() and GetNodeLocalScaling().

2、bindPoseTransform/offsetMatrix

     FBXSDK中每個mesh通常包含一個deformer(也有可能多個,比如有skin deformer,也有BlendShape deformer。目前只針對skin deformer做處理。

每個deformer下有多個cluster,看起來好像是一個骨骼,但真正的骨骼是cluster->GetLink().是這個奇怪的link。

     bindPoseTransform的作用實際上是將mesh上的頂點由mesh空間,變換到骨骼的localspace。

      step1:啊,對了,vertex在FBX裡面叫control points,這個control points 是儲存在mesh’s object space,首先把頂點從mesh的座標,先變換到世界座標下

FbxAMatrix lReferenceGlobalInitPosition;

FbxAMatrix lClusterGlobalInitPosition;

FbxAMatrix lReferenceGeometry;

FbxAMatrix lClusterRelativeInitPosition;

 cluster->GetTransformMatrix(lReferenceGlobalInitPosition);

lReferenceGeometry = GetGeometry(pMesh->GetNode());//這個geometry transform是針對在3ds Max中對pivot進行修改後會影響的值,並且該值不影響子節點。

lReferenceGlobalInitPosition *=lReferenceGeometry;

 實際上,上面得出的lReferenceGlobalInitPosition對於同一個mesh下的骨骼來說是一樣的,因為他們其實是在一個節點下的。所以可以考慮只計算一次。(當然目前是每個節點都計算了)。

        step2:把所有的vertex從世界座標變換到bonespace at binding moment

currCluster->GetTransformLinkMatrix(lClusterGlobalInitPosition);

lClusterRelativeInitPosition

= lClusterGlobalInitPosition.Inverse() * lReferenceGlobalInitPosition;

3、從動畫構造KeyFrame

      fbx sdk裡面只提供了按照時間去獲得變換矩陣的方法,所以這裡就提前將這些每個關鍵幀時間儲存下來,用fbx sdk 的方法pEvaluator->GetNodeLocalTransform(pNode, fbxtime);計算出來,然後整理成KeyFrame。