基于Unity3D三维模型的动作插值(空间关键帧动画实现)
1.引言
最近在Unity3D中实现一个基于自定义Mesh网格的骨骼动画。存储关键帧信息,然后通过插值形成中间动画。网格GameObject之间存在父子关系。插值动画对模型骨骼的Position、Sclae、Rotation三个部分分别混合插值。
并且注意,一般选取的是子骨骼相对父骨骼的Transform信息,即上述关键帧信息具体应该为Transform.localPosition、localScale、localRotation。
并且这个系统的关键帧是定义在三维空间中的,用户在一系列关键帧点之间拖动预设点,系统自动混合生成预设点位置处的模型动作信息。
演示视频地址:http://v.qq.com/page/c/f/j/c0149v62gfj.html
视频中红色的为预设点,绿色的为关键帧点,关键帧点把该关键帧定义在了一个空间位置中,预设点则计算自身到各关键帧点的距离,生成N个权重信息,进而混合成模型动画。
2.距离权重
预设点和N个空间关键帧的距离分别为d1,d2,d3...dn,我的权重函数:weight_i=1/(di^4), (靠近关键帧时收敛较快,并且整个过程较平滑),且sum_weight=weight_1 + weight_2 +weight_3 +...+weight_n
则预设对于N个空间点的权重分别是weight_i/sum_weight(i=1,2,3...N)。
Position和Scale只需要乘以权重求和即可,麻烦的是Rotation,3D空间的旋转一般采用的都是四元数,四元数具有不产生“万向节锁(Gimbal Lock)”等优良特性。下面讨论一下四元数的性质和对N个四元数的插值运算。
3.N个四元数的插值计算
四元数表示的是空间的一个轴角对((x,y,z),w),也表示成( (sin(theta/2)Nx,sin(theta/2)Ny,sin(theta/2)Nz) ,cos(theta/2) )
并且在Unity3D中,自带的Quaternion类生成的四元数为单位四元数,即使上式x^2+y^2+z^2+w^2==1,并且Nx^2+Ny^2+Nz^2==1.
那么,问题来了,对于N个四元数,我们也求得了相应的N个权重值,现在要怎么求混合值呢?
先上代码:
1 //计算四元数的幂 2 static public Quaternion quaternion_exp(Quaternion q,float exp) 3 { 4 //若w==1,则w==cos(theta/2),theta/2==360,则sin(theta/2)==0 5 //若w>1,输入的四元数不是单位四元数 6 if(q.w>=1.0f) 7 { 8 return Quaternion.identity; 9 } 10 //计算theta/2 11 float theta_2=Mathf.Acos(q.w); 12 //四元数幂为exp,等于旋转角乘以exp 13 float newTheta_2=theta_2*exp; 14 float newW=Mathf.Cos(newTheta_2); 15 //为了使得新构造的四元数也符合单位四元数定义 16 //即[w,x,y,z]=[cos(theta/2),sin(theta/2)*Nx,sin(theta/2)*Ny,sin(theta/2)*Nz] 17 float mult=Mathf.Sin(newTheta_2)/Mathf.Sin(theta_2); 18 float newX=q.x*mult; 19 float newY=q.y*mult; 20 float newZ=q.z*mult; 21 Quaternion result=new Quaternion(newX,newY,newZ,newW); 22 return result; 23 }
这段代码是四元数的幂的求法,为什么要求幂呢?
因为我们要对四元数进行插值。所谓插值,必然是权重值乘以”特定几何意义值“,然后得到合理的中间值的过程。
这里”特定几何意义值“,正是旋转的度数。
例如一个四元数q(sin60*Nx,sin60*Ny,sin60*Nz,cos60),表示绕(Nx,Ny,Nz)轴旋转30度,此时q^k则表示绕(Nx,Ny,Nz)旋转30*k度。
具体证明不赘述,推荐参考《3D数学基础:图形与游戏开发》153页。
所以到这里,我们很清楚的知道,每个关键帧提供的Rotation,最后都应该以 Rotation^(weight_i/sum_weight)的形式提供给预设点。
然后呢?对应加法的运算,在四元数这边如何体现呢?
答案是:四元数叉乘,四元数叉乘几何意义即为连接两个旋转。
所以,预设点的旋转值,混合计算的式子应该是这样的:R1^(weight_1/sum_weight)*R2^(weight_2/sum_weight)*...*Rn^(weight_n/sum_weight)。
而四元数乘法,Unity3D中提供了Quaternion的*运算符重载,直接用就是了。
posted on 2015-03-16 00:53 kyokuhuang 阅读(2975) 评论(1) 编辑 收藏 举报