(四元素)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)。但這種描述法卻不適合插值。

 

  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. }  

矩陣轉歐拉角

  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) {}  

 

歐拉角轉矩陣

 

 

  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. };  

 

四元素轉矩陣

 

  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) )  

 四元素與歐拉角的轉換都是通過矩陣來做中間跳板!

 

從一個向量旋轉到另一個向量的四元素:

  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.     {}  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章