最近做camera 的 AI,需要对四元数,欧拉角等要有一定的了解,把前面学习的整理了一下:
1。四元数的优势: 三维空间的旋转完全可以由4元数来胜任。传统意义上需要3×3矩阵来进行向量的旋转(4x4矩阵的第四列表示平移)。所以四元数更节省空间,运算速度更快。既然四元数能方便的表示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]); }
参考文献:
游戏编程精粹
LithTech引擎