基于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  阅读(2932)  评论(1编辑  收藏  举报

导航