(四元素)Quaterninos

 

(四元素)Quaterninos 

  4046人阅读  评论(0)  收藏  举报

      欧拉描述法。它使用最简单的x,y,z值来分别表示在x,y,z轴上的旋转角度,其取值为0-360(或者0-2pi),一般使用roll,pitch,yaw来表示这些分量的旋转值。需要注意的是,这里的旋转是针对世界坐标系说的,这意味着第一次的旋转不会影响第二、三次的转轴,简单的说,三角度系统无法表现任意轴的旋转,只要一开始旋转,物体本身就失去了任意轴的自主性,这也就导致了万向轴锁(Gimbal Lock)的问题。欧拉描述中针对x,y,z的旋转描述是世界坐标系下的值,所以当任意一轴旋转90°的时候会导致该轴同其他轴重合,此时旋转被重合的轴可能没有任何效果,这就是Gimbal Lock。

 

     四元素。还有一种是轴角的描述方法(即我一直以为的四元数的表示法),这种方法比欧拉描述要好,它避免了Gimbal Lock,它使用一个3维向量表示转轴和一个角度分量表示绕此转轴的旋转角度,即(x,y,z,angle),一般表示为(x,y,z,w)或者(v,w)。但这种描述法却不适合插值。

 

[cpp]  view plain copy
  1. w = cos(theta/2)  
  2. x  = ax * sin(theta/2)  
  3. y  = ay * sin(theta/2)  
  4. z  = az * sin(theta/2)  

 

      其中(ax,ay,az)表示轴的矢量,theta表示绕此轴的旋转角度,为什么是这样?和轴、角描述到底有什么不同?这是因为轴角描述的“四元组”并不是一个空间下的东西,首先(ax,ay,az)是一个3维坐标下的矢量,而theta则是级坐标下的角度,简单的将他们组合到一起并不能保证他们插值结果的稳定性,因为他们无法归一化,所以不能保证最终插值后得到的矢量长度(经过旋转变换后两点之间的距离)相等,而四元数在是在一个统一的4维空间中,方便归一化来插值,又能方便的得到轴、角这样用于3D图像的信息数据,所以用四元数再合适不过了。


      关于四元数的运算法则和推导这里有篇详细的文章介绍,重要的是一点,类似与Matrix的四元数的乘法是不可交换的,四元数的乘法的意义也类似于Matrix的乘法-可以将两个旋转合并,例如:

Q=Q1*Q2
 
表示Q的是先做Q2的旋转,再做Q1的旋转的结果,而多个四元数的旋转也是可以合并的,根据四元数乘法的定义,可以算出两个四元数做一次乘法需要16次乘法和加法,而3x3的矩阵则需要27运算,所以当有多次旋转操作时,使用四元数可以获得更高的计算效率。

 

 

关于插值

 

使用四元数的原因就是在于它非常适合插值,这是因为他是一个可以规格化的4维向量,最简单的插值算法就是线性插值,公式如:

q(t)=(1-t)q1+t q2

但这个结果是需要规格化的,否则q(t)的单位长度会发生变化,所以

q(t)=(1-t)q1+t q2 / || (1-t)q1+t q2 ||

如图:

(四元素)Quaterninos - wenpeng__851124 - 悻然的博客

 

尽管线性插值很有效,但不能以恒定的速率描述q1到q2之间的曲线,这也是其弊端,我们需要找到一种插值方法使得q1->q(t)之间的夹角θ是线性的,即θ(t)=(1-t)θ1+t*θ2,这样我们得到了球形线性插值函数q(t),如下:

q(t)=q1 * sinθ(1-t)/sinθ + q2 * sinθt/sineθ

如果使用D3D,可以直接使用D3DXQuaternionSlerp函数就可以完成这个插值过程。

 

 

矩阵,欧拉角,四元素之间的相互转换

 

矩阵转四元素:

  1. FQuat::FQuat( const FMatrix& M )  
  2. {  
  3.     //const MeReal *const t = (MeReal *) tm;  
  4.     FLOAT   s;  
  5.   
  6.     // Check diagonal (trace)  
  7.     const FLOAT tr = M.M[0][0] + M.M[1][1] + M.M[2][2];  
  8.   
  9.     if (tr > 0.0f)   
  10.     {  
  11.         FLOAT InvS = appInvSqrt(tr + 1.f);  
  12.         this->W = 0.5f * (1.f / InvS);  
  13.         s = 0.5f * InvS;  
  14.   
  15.         this->X = (M.M[1][2] - M.M[2][1]) * s;  
  16.         this->Y = (M.M[2][0] - M.M[0][2]) * s;  
  17.         this->Z = (M.M[0][1] - M.M[1][0]) * s;  
  18.     }   
  19.     else   
  20.     {  
  21.         // diagonal is negative  
  22.         INT i = 0;  
  23.   
  24.         if (M.M[1][1] > M.M[0][0])  
  25.             i = 1;  
  26.   
  27.         if (M.M[2][2] > M.M[i][i])  
  28.             i = 2;  
  29.   
  30.         static const INT nxt[3] = { 1, 2, 0 };  
  31.         const INT j = nxt[i];  
  32.         const INT k = nxt[j];  
  33.   
  34.         s = M.M[i][i] - M.M[j][j] - M.M[k][k] + 1.0f;  
  35.   
  36.         FLOAT InvS = appInvSqrt(s);  
  37.   
  38.         FLOAT qt[4];  
  39.         qt[i] = 0.5f * (1.f / InvS);  
  40.   
  41.         s = 0.5f * InvS;  
  42.   
  43.         qt[3] = (M.M[j][k] - M.M[k][j]) * s;  
  44.         qt[j] = (M.M[i][j] + M.M[j][i]) * s;  
  45.         qt[k] = (M.M[i][k] + M.M[k][i]) * s;  
  46.   
  47.         this->X = qt[0];  
  48.         this->Y = qt[1];  
  49.         this->Z = qt[2];  
  50.         this->W = qt[3];  
  51.     }  
  52. }  
  53.   
  54.   
  55. //  
  56. // MSM: Fast float inverse square root using SSE.  
  57. // Accurate to within 1 LSB.  
  58. //  
  59. FORCEINLINE FLOAT appInvSqrt( FLOAT F )  
  60. {  
  61.     const FLOAT fThree = 3.0f;  
  62.     const FLOAT fOneHalf = 0.5f;  
  63.     FLOAT temp;  
  64.   
  65.     __asm  
  66.     {  
  67.         movss   xmm1,[F]  
  68.         rsqrtss xmm0,xmm1           // 1/sqrt estimate (12 bits)  
  69.   
  70.         // Newton-Raphson iteration (X1 = 0.5*X0*(3-(Y*X0)*X0))  
  71.         movss   xmm3,[fThree]  
  72.         movss   xmm2,xmm0  
  73.         mulss   xmm0,xmm1           // Y*X0  
  74.         mulss   xmm0,xmm2           // Y*X0*X0  
  75.         mulss   xmm2,[fOneHalf]     // 0.5*X0  
  76.         subss   xmm3,xmm0           // 3-Y*X0*X0  
  77.         mulss   xmm3,xmm2           // 0.5*X0*(3-Y*X0*X0)  
  78.         movss   [temp],xmm3  
  79.     }  
  80.   
  81.     return temp;  
  82. }  

矩阵转欧拉角

[cpp]  view plain copy
  1. FRotator FMatrix::Rotator() const  
  2. {  
  3.     const FVector       XAxis   = GetAxis( 0 );  
  4.     const FVector       YAxis   = GetAxis( 1 );  
  5.     const FVector       ZAxis   = GetAxis( 2 );  
  6.   
  7.     FRotator    Rotator = FRotator(   
  8.                                     appRound(appAtan2( XAxis.Z, appSqrt(Square(XAxis.X)+Square(XAxis.Y)) ) * 32768.f / PI),   
  9.                                     appRound(appAtan2( XAxis.Y, XAxis.X ) * 32768.f / PI),   
  10.                                     0   
  11.                                 );  
  12.       
  13.     const FVector       SYAxis  = FRotationMatrix( Rotator ).GetAxis(1);  
  14.     Rotator.Roll        = appRound(appAtan2( ZAxis | SYAxis, YAxis | SYAxis ) * 32768.f / PI);  
  15.     return Rotator;  
  16. }  
  17.   
  18. FRotator( INT InPitch, INT InYaw, INT InRoll )  
  19.     :   Pitch(InPitch), Yaw(InYaw), Roll(InRoll) {}  

 

欧拉角转矩阵

 

 

[cpp]  view plain copy
  1. FRotationMatrix(const FRotator& Rot)  
  2.     {  
  3.         const FLOAT SR  = GMath.SinTab(Rot.Roll);  
  4.         const FLOAT SP  = GMath.SinTab(Rot.Pitch);  
  5.         const FLOAT SY  = GMath.SinTab(Rot.Yaw);  
  6.         const FLOAT CR  = GMath.CosTab(Rot.Roll);  
  7.         const FLOAT CP  = GMath.CosTab(Rot.Pitch);  
  8.         const FLOAT CY  = GMath.CosTab(Rot.Yaw);  
  9.   
  10.         M[0][0] = CP * CY;  
  11.         M[0][1] = CP * SY;  
  12.         M[0][2] = SP;  
  13.         M[0][3] = 0.f;  
  14.   
  15.         M[1][0] = SR * SP * CY - CR * SY;  
  16.         M[1][1] = SR * SP * SY + CR * CY;  
  17.         M[1][2] = - SR * CP;  
  18.         M[1][3] = 0.f;  
  19.   
  20.         M[2][0] = -( CR * SP * CY + SR * SY );  
  21.         M[2][1] = CY * SR - CR * SP * SY;  
  22.         M[2][2] = CR * CP;  
  23.         M[2][3] = 0.f;  
  24.   
  25.         M[3][0] = 0.f;  
  26.         M[3][1] = 0.f;  
  27.         M[3][2] = 0.f;  
  28.         M[3][3] = 1.f;  
  29.     }  
  30. class FGlobalMath  
  31. {  
  32. public:  
  33.     // Constants.  
  34.     enum {ANGLE_SHIFT   = 2};       // Bits to right-shift to get lookup value.  
  35.     enum {ANGLE_BITS    = 14};      // Number of valid bits in angles.  
  36.     enum {NUM_ANGLES    = 16384};   // Number of angles that are in lookup table.  
  37.     enum {ANGLE_MASK    =  (((1<<ANGLE_BITS)-1)<<(16-ANGLE_BITS))};  
  38.   
  39.     // Basic math functions.  
  40.     FORCEINLINE FLOAT SinTab( int i ) const  
  41.     {  
  42.         return TrigFLOAT[((i>>ANGLE_SHIFT)&(NUM_ANGLES-1))];  
  43.     }  
  44.     FORCEINLINE FLOAT CosTab( int i ) const  
  45.     {  
  46.         return TrigFLOAT[(((i+16384)>>ANGLE_SHIFT)&(NUM_ANGLES-1))];  
  47.     }  
  48.     FLOAT SinFloat( FLOAT F ) const  
  49.     {  
  50.         return SinTab(appTrunc((F*65536.f)/(2.f*PI)));  
  51.     }  
  52.     FLOAT CosFloat( FLOAT F ) const  
  53.     {  
  54.         return CosTab(appTrunc((F*65536.f)/(2.f*PI)));  
  55.     }  
  56.   
  57.     // Constructor.  
  58.     FGlobalMath();  
  59.   
  60. private:  
  61.     // Tables.  
  62.     FLOAT  TrigFLOAT        [NUM_ANGLES];  
  63. };  

 

四元素转矩阵

 

[cpp]  view plain copy
  1. FQuatRotationTranslationMatrix(const FQuat& Q, const FVector& Origin)  
  2. {  
  3.     const FLOAT x2 = Q.X + Q.X;  const FLOAT y2 = Q.Y + Q.Y;  const FLOAT z2 = Q.Z + Q.Z;  
  4.     const FLOAT xx = Q.X * x2;   const FLOAT xy = Q.X * y2;   const FLOAT xz = Q.X * z2;  
  5.     const FLOAT yy = Q.Y * y2;   const FLOAT yz = Q.Y * z2;   const FLOAT zz = Q.Z * z2;  
  6.     const FLOAT wx = Q.W * x2;   const FLOAT wy = Q.W * y2;   const FLOAT wz = Q.W * z2;  
  7.   
  8.     M[0][0] = 1.0f - (yy + zz); M[1][0] = xy - wz;              M[2][0] = xz + wy;          M[3][0] = Origin.X;  
  9.     M[0][1] = xy + wz;          M[1][1] = 1.0f - (xx + zz);     M[2][1] = yz - wx;          M[3][1] = Origin.Y;  
  10.     M[0][2] = xz - wy;          M[1][2] = yz + wx;              M[2][2] = 1.0f - (xx + yy); M[3][2] = Origin.Z;  
  11.     M[0][3] = 0.0f;             M[1][3] = 0.0f;                 M[2][3] = 0.0f;             M[3][3] = 1.0f;  
  12. }  
  13.   
  14. QuatRotationTranslationMatrix( *this, FVector(0.f) )  

 四元素与欧拉角的转换都是通过矩阵来做中间跳板!

 

从一个向量旋转到另一个向量的四元素:

[cpp]  view plain copy
  1. FQuat FQuatFindBetween(const FVector& vec1, const FVector& vec2)  
  2. {  
  3.     const FVector cross = vec1 ^ vec2;  
  4.     const FLOAT crossMag = cross.Size();  
  5.   
  6.     if(crossMag < KINDA_SMALL_NUMBER)  
  7.     {  
  8.         const FLOAT Dot = vec1 | vec2;  
  9.         if(Dot > -KINDA_SMALL_NUMBER)  
  10.         {  
  11.             return FQuat::Identity; // no rotation  
  12.         }  
  13.         else  
  14.         {  
  15.             // rotation by 180 degrees around a vector orthogonal to vec1 & vec2  
  16.             FVector Vec = vec1.SizeSquared() > vec2.SizeSquared() ? vec1 : vec2;  
  17.             Vec.Normalize();  
  18.   
  19.             FVector AxisA, AxisB;  
  20.             Vec.FindBestAxisVectors(AxisA, AxisB);  
  21.   
  22.             return FQuat(AxisA.X, AxisA.Y, AxisA.Z, 0.f); // (axis*sin(pi/2), cos(pi/2)) = (axis, 0)  
  23.         }  
  24.     }  
  25.   
  26.     FLOAT angle = appAsin(crossMag);  
  27.   
  28.     const FLOAT dot = vec1 | vec2;  
  29.     if(dot < 0.0f)  
  30.     {  
  31.         angle = PI - angle;  
  32.     }  
  33.   
  34.     const FLOAT sinHalfAng = appSin(0.5f * angle);  
  35.     const FLOAT cosHalfAng = appCos(0.5f * angle);  
  36.     const FVector axis = cross / crossMag;  
  37.   
  38.     return FQuat(  
  39.         sinHalfAng * axis.X,  
  40.         sinHalfAng * axis.Y,  
  41.         sinHalfAng * axis.Z,  
  42.         cosHalfAng );  
  43. }  
  44.   
  45. #define KINDA_SMALL_NUMBER  (1.e-4)  
  46.   
  47. FQuat( FLOAT InX, FLOAT InY, FLOAT InZ, FLOAT InA )  
  48.     :   X(InX), Y(InY), Z(InZ), W(InA)  
  49.     {}  
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值