骨骼动画驱动的一般方法,以及使用FBX SDK的一些实现

我终于要更新博客了,进公司之后每天学的做的很多,但真正有时间去总结的时候很少。之后的话尽量每周都要做一个知识的总结和积累吧,一方面自己真的是做过就忘,另一方面也能够将自己踩到的坑,遇到的问题与大家分享一下。

入职三个月吧第一个月最主要的事情就是在骨骼动画的驱动,一开始是研究的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。




猜你喜欢

转载自blog.csdn.net/u011310341/article/details/80791411