四元数及其平滑处理

最近做camera 的 AI,需要对四元数,欧拉角等要有一定的了解,把前面学习的整理了一下:

1。四元数的优势

 三维空间的旋转完全可以由4元数来胜任。传统意义上需要3×3矩阵来进行向量的旋转(4×4矩阵的第四列表示平移)。所以四元数更节省空间,运算速度更快。既然四元数能方便的表示3D旋转,那么对他们进行插值就能产生平滑的旋转效果。

劣势可能是比较抽象,不大好理解。而且据说顶点变换还是矩阵效率更高(涉及到平移)。

2。四元数的物理意义

Q( x, y, z, w)来表示向量 绕轴 A(ax, ay,az) 旋转alpha

则: x = sin(alpha/2)*ax;

y =   sin(alpha/2)*ay;

z =  sin(alpha/2)*az;

w = cos(alpha/2);

 

3。四元素的数学意义

我们知道 复数的表示为 a+bi; 其中i*i = -1;

四元数 q = w + xi + yi +zi;

复数的运算法则为四元数的数学运算提供了规则基础。比如说乘法,加法等。

4。四元数和矩阵间的转换

QX =0; QY=1;QZ=2;QW=3;

void quat_ConvertFromMatrix(float *pQuat, const float mat[4][4])
{
 float diag, s;
 int i, j, k;

 diag = mat[0][0] + mat[1][1] + mat[2][2];

 if(diag < -0.999f )
 {
  i = QX;
  if( mat[QY][QY] > mat[QX][QX] )
   i = QY;
  if( mat[QZ][QZ] > mat[i][i] )
   i = QZ;

  j = g_QNext[i];
  k = g_QNext[j];

  s = ltsqrtf( mat[i][i] - ( mat[j][j] + mat[k][k] ) + /*mat[3][3]*/ 1.0f );

  pQuat[i] = s * 0.5f;
  s = 0.5f / s;
  pQuat[QW] = ( mat[k][j] - mat[j][k] ) * s;
  pQuat[j] = ( mat[j][i] + mat[i][j] ) * s;
  pQuat[k] = ( mat[k][i] + mat[i][k] ) * s;
  return;
 }

 s = ltsqrtf( diag + /*mat[3][3]*/ 1.0f );

 pQuat[3] = s * 0.5f;
 s = 0.5f / s;

 pQuat[0] = (mat[2][1] - mat[1][2]) * s;
 pQuat[1] = (mat[0][2] - mat[2][0]) * s;
 pQuat[2] = (mat[1][0] - mat[0][1]) * s;
}

void quat_ConvertToMatrix(const float *pQuat, float mat[4][4])
{
 float s, xs, ys, zs, wx, wy, wz, xx, xy, xz, yy, yz, zz;

/*!
  get the values for matrix calcuation.
*/
 s = 2.0f / ((pQuat[0] * pQuat[0]) + (pQuat[1] * pQuat[1]) + 
  (pQuat[2] * pQuat[2]) + (pQuat[3] * pQuat[3]));

 xs = pQuat[0] * s;
 ys = pQuat[1] * s;
 zs = pQuat[2] * s;

 wx = pQuat[3] * xs;
 wy = pQuat[3] * ys;
 wz = pQuat[3] * zs;

 xx = pQuat[0] * xs;
 xy = pQuat[0] * ys;
 xz = pQuat[0] * zs;

 yy = pQuat[1] * ys;
 yz = pQuat[1] * zs;

 zz = pQuat[2] * zs;

/*!
  Fill in matrix

*/
 mat[0][0] = 1.0f - (yy + zz);
 mat[0][1] = xy - wz;
 mat[0][2] = xz + wy;

 mat[1][0] = xy + wz;
 mat[1][1] = 1.0f - (xx + zz);
 mat[1][2] = yz - wx;

 mat[2][0] = xz - wy;
 mat[2][1] = yz + wx;
 mat[2][2] = 1.0f - (xx + yy);

 mat[0][3] = mat[1][3] = mat[2][3] = mat[3][0] = mat[3][1] = mat[3][2] = 0.0f;
 mat[3][3] = 1.0f;
}

 

具体推倒过程可以参考相关文献。

5。四元数插值

有很多插值方法,比如线性,球形,样条插值等;

lerp (t;,q0,q1) = (1-t)q0 + tq1 ;//快速,但动画不平滑,需要归一化 / ||(1-t)q0 + tq1||;

slerp( t;, q0,q1) = [q0 *sin(thata(1-thata)) + q1sin(thata*t)] / sin(thata); //平滑,归一化;

thata 为q0 q1夹角。q0 dot q1 = cos(thata);

相关代码:

void quat_Slerp(float *pDest, const float *pQ1, const float *pQ2, float t)
{
 float rot1q[4];
 float omega, cosom, oosinom;
 float scalerot0, scalerot1;

/*!
  Calculate the cosine

*/
 cosom = pQ1[0]*pQ2[0] + pQ1[1]*pQ2[1] + pQ1[2]*pQ2[2] + pQ1[3]*pQ2[3];

/*!
  adjust signs if necessary

*/
 if(cosom < 0.0f)
 {
  cosom = -cosom;
  rot1q[0] = -pQ2[0];
  rot1q[1] = -pQ2[1];
  rot1q[2] = -pQ2[2];
  rot1q[3] = -pQ2[3];
 }
 else  
 {
  rot1q[0] = pQ2[0];
  rot1q[1] = pQ2[1];
  rot1q[2] = pQ2[2];
  rot1q[3] = pQ2[3];
 }

/*!
  calculate interpolating coeffs

*/
 if ( (1.0f - cosom) > 0.0001f ) 
 { 
/*!
   standard case

*/
  omega   = ltacosf(cosom);
  oosinom = 1.0f / ltsinf(omega);
  scalerot0 = ltsinf((1.f - t) * omega) * oosinom;
  scalerot1 = ltsinf(t * omega) * oosinom;
 }
 else
 { 
/*!
   rot0 and rot1 very close - just do linear interp.

*/
  scalerot0 = 1.0f - t;
  scalerot1 = t;
 }

 //! build the new quarternion
 pDest[0] = (scalerot0 * pQ1[0] + scalerot1 * rot1q[0]);
 pDest[1] = (scalerot0 * pQ1[1] + scalerot1 * rot1q[1]);
 pDest[2] = (scalerot0 * pQ1[2] + scalerot1 * rot1q[2]);
 pDest[3] = (scalerot0 * pQ1[3] + scalerot1 * rot1q[3]);
}

 

参考文献:

游戏编程精粹

 

本文转自:http://blog.csdn.net/hziee_/article/details/1630116

 

 

 

发表评论

电子邮件地址不会被公开。