FBX BlendShape/Morph动画解析
目前fbx 2015.1中支持三种变形器:skinDeformer,blendShapeDeformer,vertexCacheDeformer。定义在fbxdeformer.h中:
enum EDeformerType {
eUnknown, //!< Unknown deformer type
eSkin, //!< Type FbxSkin
eBlendShape, //!< Type FbxBlendShape
eVertexCache //!< Type FbxVertexCacheDeformer
};
前两种变形器分别对应:骨骼蒙皮动画 和 变形动画,(第三种还没研究)。
相应的播放代码都可以在ViewScene示例中找到。
其中 骨骼蒙皮动画 游戏里用得最多,所以研究得较早。这几天又看了一下 变形动画(BlendShape/Morph animation),散乱记录一下若干注意事项。
一,变形(blendShape/morph)基本原理。
大体上讲就是在拓扑结构相同的mesh之间插值。细节在下文中会提到。
典型应用是做表情动画。
二,在3dmax中制作带有 变形动画 的模型。
我用的是3dmax 2012(中文版)。
1,先建一个模型X,然后复制出一份X1。
2,对X1在保持拓扑结构不变的条件下进行变形。
3,为X添加“变形器”修改器,将X1添加到“变形器”修改器的一个通道中,则X1成为X的一个变形目标。
4,调整通道值,便可看到X受X1影响发生变形。
5,打开自动关键点,记录变形动画。
可以重复上面方法为X添加多个变形目标。
“变形器”修改器的一个通道可以添加多个变形目标,通道名称将显示为此通道中所添加的第一个变形目标的名称,此通道所添加的所有变形目标可以在”渐进变形“栏的”目标列表“中看到。
当一个通道中添加多个变形目标时,称为progressive morph,X将按目标列表中目标的顺序依次变形。
另外注意,并非对X1的任何保持拓扑的修改都能对X产生影响,只有局部空间的修改(例如使用对象空间修改器进行的修改、直接编辑顶点的修改等)才起作用,而对X1整体进行的修改(例如对X1整体进行旋转、平移、缩放等)则不会起作用。
三,fbx变形动画原理。
fbx sdk的ViewScene示例中的ComputeShapeDeformation()函数实现变形动画的数据提取和播放,通过阅读这部分代码,可以了解fbx中变形动画数据的逻辑结构:
一个mesh包含多个blendShape,
一个blendShape包含多个blendShapeChannel,
一个blendShapeChannel包含多个targetShape和一个weight动画曲线weightCurve。
targetShape中包含controlPoints和一个fullWeight值。
这些结构与3dmax中的对应关系如下:
mesh对应的就是3dmax中的模型,
blendShape对应“变形器”修改器,一个模型可以添加多个“变形器”修改器,
blendShapeChannel对应“变形器”修改器的通道,blendShapeChannel->GetName()得到通道名称.
targetShape对应变形目标,一个通道可以添加多个变形目标。targetShape->GetName()得到变形目标名称。
通道的weightCurve给出各时间点此通道变形进度weight。
targetShape的fullWeight值表示的是本通道变形进度weight值达到多少时恰好完全变形为本targetShape。对于只有一个targetShape的通道,targetShape的fullWeight必定是100;而对于含有多个targetShape的通道,各targetShape的fullWeight值按顺序依次增大,并且最后一个targetShape的fullWeight必定是100。
可见,非常符合直观,至此变形插值算法几乎不用再看ComputeShapeDeformation()的代码便可以直接想象出来了:
1,对于只有一个变形目标的通道(非渐近变形),只要根据通道变形进度weight在变形物体和变形目标间插值。
2,对于含有多个变形目标的通道(渐近变形),看变形进度weight落在哪两个变形目标的fullWeight之间,然后计算weight将此区间分成的比例得到此区间上的变形进度weightOfSpan,再根据此区间变形进度在上述两个变形目标间插值。
下面是ComputeShapeDeformation()中的注释,用具体例子说明在progressive morph和progressive morph情况下的插值算法,与我们的直观想象完全一致:
If there is only one targetShape on this channel, the influence is easy to calculate:
influence = (targetShape - baseGeometry) * weight * 0.01
dstGeometry = baseGeometry + influence
But if there are more than one targetShapes on this channel, this is an in-between
blendshape, also called progressive morph. The calculation of influence is different.
For example, given two in-between targets, the full weight percentage of first target
is 50, and the full weight percentage of the second target is 100.
When the weight percentage reach 50, the base geometry is already be fully morphed
to the first target shape. When the weight go over 50, it begin to morph from the
first target shape to the second target shape.
To calculate influence when the weight percentage is 25:
1. 25 falls in the scope of 0 and 50, the morphing is from base geometry to the first target.
2. And since 25 is already half way between 0 and 50, so the real weight percentage change to
the first target is 50.
influence = (firstTargetShape - baseGeometry) * (25-0)/(50-0) * 100
dstGeometry = baseGeometry + influence
To calculate influence when the weight percentage is 75:
1. 75 falls in the scope of 50 and 100, the morphing is from the first target to the second.
2. And since 75 is already half way between 50 and 100, so the real weight percentage change
to the second target is 50.
influence = (secondTargetShape - firstTargetShape) * (75-50)/(100-50) * 100
dstGeometry = firstTargetShape + influence
四,多通道同时影响时的叠加方式。
假设变形物体X受channel1和channel2两个通道影响,channel1中只有一个变形目标shape1,通道变形进度为w1;channel2中只有一个变形目标shape2,通道变形进度为w2。设v是X上一点,原始坐标为p;v1是shape1上等位点,坐标为p1;v2是shape2上等位点,坐标为p2。
则v在两个通道影响下变形后的坐标p_deformed应计算如下:
p_deformedByChannel1=p+(p1-p)*w1
p_deformedByChannel1andChannel2=p_deformedByChannel1+(p2-p_deformedByChannel1)*w2
p_deformed=p_deformedByChannel1andChannel2
五,法线问题。
对变形物体进行变形,如果只是顶点位置发生变化,而法线不变的话,那么在有光照的情况下显示效果是不对的,所以法线也需要在变形物体和变形目标之间进行插值。
前面分析fbx变形动画数据逻辑结构时提到“targetShape中包含controlPoints和一个fullWeight值”,是因为在viewScene示例中,只使用targetShape的controlPoints对变形物体的顶点位置进行了变形,而根本没有处理法线的变形。我没有找到由targetShape获得有效normals的方法。而且当我把动画导出为ascii的fbx,查看targetShape的normals数据时发现全是0。
我目前的解决办法是通过targetShape获得targetShapeName,然后再按名称在场景中搜索到与targetShape相对应的模型(mesh),然后从mesh中取得法线数据。这样就要求变形目标模型不能删,且要随变形物体一同导出到fbx文件中。另外一个需要注意的问题是,由于我的引擎中只支持三角网,于是要求或者在3dmax建模时物体就建成三角网,或者在导出为fbx文件时勾选“三角化”选项,或者在引擎载入fbx文件之后调用fbx sdk提供的api转换为三角网。但是后两者都不能保证变形物体mesh和变形目标mesh在三角化后仍具有完全相同的拓扑结构。而如果变形物体mesh和变形目标mesh的拓扑结构不同,法线插值就无法进行。
注意:顶点位置插值是不要求变形物体mesh和变形目标mesh拓扑结构相同的,只要顶点能一一对应,顶点位置插值就可以进行,所以如果像ViewScene示例那样只处理顶点变形而不考虑法线变形的话,不必要求模型在3dmax中就建成三角网。(不需要考虑法线变形的情况确实是存在的,例如的像《地铁跑酷》那种无光照的Q版3d游戏,根本不需要法线,因此做变形时也不用考虑法线变形)。
法线插值是向量间插值,与顶点位置插值(点之间插值)是不同的。点之间的插值直接线性插值即可,而向量间插值严格来讲应该使用球面插值(slerp)。不过经过试验,发现在此处两种方法的视觉差异并不很大,且线性插值速度要快得多,所以最后我仍然选择了线性插值。
六,ViewScene示例中变形动画代码有错误。
ViewScene示例中播放变形动画的代码是有错误的,将动画由3dmax中导出为fbx再用viewScene示例播放,会发现viewScene示例播放结果与3dmax中不一样,而且动画会出现跳变等明显错误。
我现在已经记不清具体是什么原因造成的了,但我在自己的播放程序中解决了这些问题。
我在c3dEngine2(https://github.com/wantnon2/c3dEngine2)中添加了一个app_fbxLoader_shapeDeformTest demo,效果视频:
FBX BlendShape/Morph动画解析的更多相关文章
- Skeleton with Assimp 骨骼动画解析
Skeleton with Assimp 骨骼动画解析 骨骼动画是图形学中十分常见应用很广泛的一个技术,也是比较基础的内容,作为图形学的工程师需要将这一部分内容梳理清晰,主要关键在于几点:第一,分清楚 ...
- Android动画解析--XML
动画类型 Android的animation由四种类型组成 XML中 alpha 渐变透明度动画效果 scale 渐变尺寸伸缩动画效果 translate 画面转换位置移动动画效果 rotate 画面 ...
- 【转】Unity3d:读取FBX中的动画
从模型中获得切割好的动画clip,并且对其中设置好的动画事件进行修改方法: 1.动画模型后缀为.FBX.在Unity3d中,能够显示FBX中的动画.要加载模型中的AnimationClip,只要Ass ...
- Unity3d修改FBX文件的动画名方法
问题描述:FBX文件导入Unity3d后的动画名字一般都是 “Take 001”并且无法修改!如何修改它呢? 解决方法:解决方法其实很简单,只要你按照Unity3d的FBX文件命名规则,压根就不会存在 ...
- Unity导入FBX自动进行动画切分
手动处理动画分割 在导入FBX模型过程中,若带有动画呢,需要对它进行切分. 当然这个工作可以在Unity中完成. 比如: 这样手动来分割进行. 自动动画切分 这就需要代码了. 把代码保存成cs文件 ...
- 3DMAX导出FBX的烘焙动画选项
勾选了 [烘焙动画]选项时,表示由骨骼动画塌陷为逐帧动画,这样的结果就是:导出的动画确保是正确的,但体积增大,这是骨骼动画与逐帧去画的区别所在. 如果不勾选此选项,则导出的是骨骼动画,可能出现一些问题 ...
- android动画解析(初级)
效果图: ObjectAnimator继承自ValueAnimator的,底层的动画实现机制也是基于ValueAnimator来完成的,因此ValueAnimator仍然是整个属性动画当中最核心的一个 ...
- CATransition 转场动画解析
http://blog.csdn.net/mad2man/article/details/17260901
- Unity3D之多个fbx导入场景, 合并多个动画
1:先导入到合适的文件夹, Unity自动刷新, 生成相应的文件. 2:在Project视图中选中单个fbx, 在Inspector中选择"Rig", 更改"Animat ...
随机推荐
- Discuz常见大问题-如何使用云采集插件
在百度中搜discuz 云采集插件(直接在Discuz官方的插件库中搜可能搜不到) 启用这个插件 随后在任意版块发帖的时候,顶部都会有一个文本框,可以直接放别人博客的帖子,点击获取将生成到你发的帖子中 ...
- Cass环境下光标无显示
先安装CAD2004,十字光标正常显示:再安装CASS7.0,光标就不显示了.现在不清楚是CAD的问题,还是CASS的问题,多半是后者.重新配置了CASS环境也不行. 于是,打开CAD选项,显示,窗口 ...
- layer获取iframe内容
var layer1 = function() { layer.open({ type: 2, title: '标题', maxmin: false, area: ['780px', '600px'] ...
- leetcode Wildcard Matching greedy algrithm
The recursive program will result in TLE like this: class Solution { public: bool isMatch(const char ...
- Nhibernate Criteria 多个or条件查询
sql: select * from table t where (t.name like '%张三%' or t.schoolName like '%张三%' or t.cityname like ...
- Java程序猿学习C++之数组和动态数组
数组: #include <iostream> using namespace std; //模板函数 template <class T> void dump(T val) ...
- 科普:TLS、SSL、HTTPS以及证书(转)
最近在研究基于ssl的传输加密,涉及到了key和证书相关的话题,走了不少弯路,现在总结一下做个备忘 不少人可能听过其中的超过3个名词,但它们究竟有什么关联呢? TLS是 传输层安全协议(Transpo ...
- lua入门之二:c/c++ 调用lua及多个函数返回值的获取
当 Lua 调用 C 函数的时候,使用和 C 调用 Lua 同样类型的栈来交互. C 函数从栈中获取她的參数.调用结束后将返回结果放到栈中.为了区分返回结果和栈中的其它的值,每一个 C 函数还会返回结 ...
- redis通过json方案存取对象com.alibaba.fastjson.JSONException: syntax error, expect
问题描述: redis基于json方案存取对象时报错:com.alibaba.fastjson.JSONException: syntax error, expect com.alibaba.fast ...
- HDUOJ ---1423 Greatest Common Increasing Subsequence(LCS)
Greatest Common Increasing Subsequence Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536 ...