zoukankan      html  css  js  c++  java
  • 基于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的*运算符重载,直接用就是了。

  • 相关阅读:
    dubbo注册zookeeper保错原因
    Django 终端打印SQL语句
    Django 的orm模型
    Django 的路由系统
    Django 开端
    前端 jq的ajax请求
    前端 后台
    前端 JQ操作
    前端 链式操作
    前端 JQ事件操作
  • 原文地址:https://www.cnblogs.com/kyokuhuang/p/4340902.html
Copyright © 2011-2022 走看看