OpenGL ES 從零開始系列9:動畫基礎和關鍵幀動畫
阿新 • • 發佈:2019-02-08
這另外的一個方法相對簡單些。並且這個基本演算法來自於Matrix FAQ,雖然我需要把它轉換成行優先的順序。
static inline void Matrix3DSetUsingQuaternion3D(Matrix3D matrix, Quaternion3D quat)
{
matrix[0] = (1.0f - (2.0f * ((quat.y * quat.y) + (quat.z * quat.z))));
matrix[1] = (2.0f * ((quat.x * quat.y) - (quat.z * quat.w)));
matrix[2] = (2.0f * ((quat.x * quat.z) + (quat.y * quat.w)));
matrix[3] = 0.0f;
matrix[4] = (2.0f * ((quat.x * quat.y) + (quat.z * quat.w)));
matrix[5] = (1.0f - (2.0f * ((quat.x * quat.x) + (quat.z * quat.z))));
matrix[6] = (2.0f * ((quat.y * quat.z) - (quat.x * quat.w)));
matrix[7] = 0.0f;
matrix[8] = (2.0f * ((quat.x * quat.z) - (quat.y * quat.w)));
matrix[9] = (2.0f * ((quat.y * quat.z) + (quat.x * quat.w)));
matrix[10] = (1.0f - (2.0f * ((quat.x * quat.x) + (quat.y * quat.y))));
matrix[11] = 0.0f;
matrix[12] = 0.0f;
matrix[13] = 0.0f;
matrix[14] = 0.0f;
matrix[15] = 1.0f;
}
Converting an Angle and Axis of Rotation to a Quaternion 把一個角度和旋轉軸轉換成一個四元數
static inline Quaternion3D Quaternion3DMakeWithAxisAndAngle(Vector3D axis, GLfloat angle)
{
Quaternion3D quat;
GLfloat sinAngle;
angle *= 0.5f;
Vector3DNormalize(&axis);
sinAngle = sinf(angle);
quat.x = (axis.x * sinAngle);
quat.y = (axis.y * sinAngle);
quat.z = (axis.z * sinAngle);
quat.w = cos(angle);
return quat;
}
Extracting an Angle and Axis of Rotation from a Quaternion 從一個四元數中檢測角度和軸得旋轉
反過來,我們也可以從四元數中取得旋轉的資料,包括旋轉角度和深度,就像這樣
static inline void Quaternion3DExtractAxisAndAngle(Quaternion3D quat, Vector3D *axis, GLfloat *angle)
{
GLfloat s;
Quaternion3DNormalize(&quat);
s = sqrtf(1.0f - (quat.w * quat.w));
if (fabs(s) < 0.0005f) s = 1.0f;
if (axis != NULL)
{
axis->x = (quat.x / s);
axis->y = (quat.y / s);
axis->z = (quat.z / s);
}
if (angle != NULL)
*angle = (acosf(quat.w) * 2.0f);
}
Quaternion Multiplication 四元數乘法
為了合併兩種不同形式的四元數中得3D旋轉資訊。我們只需要讓他們彼此相乘。好了繼續我們得程式碼
static inline void Quaternion3DMultiply(Quaternion3D *quat1, Quaternion3D *quat2)
{
Vector3D v1, v2, cp;
float angle;
v1.x = quat1->x;
v1.y = quat1->y;
v1.z = quat1->z;
v2.x = quat2->x;
v2.y = quat2->y;
v2.z = quat2->z;
angle = (quat1->w * quat2->w) - Vector3DDotProduct(v1, v2);
cp = Vector3DCrossProduct(v1, v2);
v1.x *= quat2->w;
v1.y *= quat2->w;
v1.z *= quat2->w;
v2.x *= quat1->w;
v2.y *= quat1->w;
v2.z *= quat1->w;
quat1->x = v1.x + v2.x + cp.x;
quat1->y = v1.y + v2.y + cp.y;
quat1->z = v1.z + v2.z + cp.z;
quat1->w = angle;
}
Inverting a Quaternion 四元數轉置
我們通過做一個四元數的共軛運算來取得四元數的轉置。四元數做共軛運算其實就是將四元數中表示向量(x,y,z)的值取反。在這裡的實現中,我們把它[四元數轉置計算]作為四元數標準計算的一部分,而不是一個獨立的步驟:
static inline void Quaternion3DInvert(Quaternion3D *quat)
{
GLfloat length = 1.0f / ((quat->x * quat->x) +
(quat->y * quat->y) +
(quat->z * quat->z) +
(quat->w * quat->w));
quat->x *= -length;
quat->y *= -length;
quat->z *= -length;
quat->w *= length;
}
Creating a Quaternion from Euler Angles 從尤拉角中建立四元數
前面我說過在旋轉中最好不要使用尤拉角,但是有時候我們需要將尤拉角轉換成四元數,比如說使用者輸入的資訊是尤拉角資訊。轉換的步驟是,將尤拉軸用Vector3D表示出來,然後將Vector3D的值轉換成四元數,最後將四元數相乘來得到結果:
static inline Quaternion3D Quaternion3DMakeWithEulerAngles(GLfloat x, GLfloat y, GLfloat z)
{
Vector3D vx = Vector3DMake(1.f, 0.f, 0.f);
Vector3D vy = Vector3DMake(0.f, 1.f, 0.f);
Vector3D vz = Vector3DMake(0.f, 0.f, 1.f);
Quaternion3D qx = Quaternion3DMakeWithAxisAndAngle(vx, x);
Quaternion3D qy = Quaternion3DMakeWithAxisAndAngle(vy, y);
Quaternion3D qz = Quaternion3DMakeWithAxisAndAngle(vz, z);
Quaternion3DMultiply(&qx, &qy );
Quaternion3DMultiply(&qx, &qz );
return qx;
}