我终于要更新博客了,进公司之后每天学的做的很多,但真正有时间去总结的时候很少。之后的话尽量每周都要做一个知识的总结和积累吧,一方面自己真的是做过就忘,另一方面也能够将自己踩到的坑,遇到的问题与大家分享一下。
入职三个月吧第一个月最主要的事情就是在骨骼动画的驱动,一开始是研究的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。