概述
旋转角度直接影响OpenGL GL_MODELVIEW矩阵的前三列,准确地说是向左、向上与向前三轴元素。例如,如果一沿X轴的单位向量(1,0,0)与任一3×3旋转矩阵相乘,旋转后的向量结果为:
也就是说,旋转矩阵的第一列(m0,m1,m2)表示旋转后的做轴。同样,第二列为上轴,第三列为前轴。
本文描述用旋转角度构造GL_MODELVIEW矩阵。
旋转轴
首先,我们看一看绕每个轴旋转:+X、+Y与+Z。我们以三种不同方式将三个轴投影到到一个平面,将要旋转的轴朝向你。正旋转方向为逆时针(右手规则)。
绕左(X)轴旋转(Pitch)
上轴(Y)与前轴(Z)的初始值为(0,1,0)与(0,0,1)。如果左轴(X)旋转A°,因此新的上轴从(1,0,0)变换为X‘为(0,cosA,sinA),新的前轴(Z')变为(0,-sinA,cosA)。新轴作为3×3旋转矩阵的列分量插入近来。旋转矩阵变为:
绕上(Y)轴旋转(Yaw,Heading)
绕上轴(Y)旋转
现在,我们旋转朝向你的向上向量B°。左(X)轴从(1,0,0)变为X’(cosB,0,-SinB)。前轴(Z)从(0,0,1)变换为Z‘(sinB,0,cosB)。
绕前轴(Z)旋转(Roll)
如果我们旋转前轴(Z)C°,初始左轴(1,0,0)变为X’(cosC,sinC,0),上轴(0,1,0)变为Y‘(-sinC,cosC,0)。
角轴
我们可以通过上面3个矩阵相乘的方式将这些分开的轴旋转结合成一个矩阵。注意矩阵乘法是不可呼唤的,因此,矩阵乘法的不同顺序产生不同的结果。共有6中不同的结合方式:RxRyRz、RxRzRy、 RyRxRz、RyRzRx、RzRxRy与RzRyRx。
组合旋转矩阵的左列为旋转后的左轴,中间列为上轴,右列为前轴。
下面是RxRyRz结合的C++实例代码。它以Rz(roll)、Ry(yaw)、Rx(pitch)顺序执行3个旋转操作。左轴、上轴与前轴的结果可以用于构造GL_MODELVIEW矩阵。
struct Vector3 { float x; float y; float z; Vector3() : x(0), y(0), z(0) {}; // 创建时初始化 }; /////////////////////////////////////////////////////////////////////////////// //将欧拉(x,y,z)转换到(左, 上, 前) // 旋转矩阵的列代表左轴、上轴与前轴。 // 旋转顺序为:Roll->Yaw->Pitch (Rx*Ry*Rz) // Rx: 绕X轴旋转, pitch // Ry: 绕Y轴旋转, yaw(heading) // Rz: 绕Z轴旋转, roll // Rx Ry Rz // |1 0 0| | Cy 0 Sy| |Cz -Sz 0| | CyCz -CySz Sy | // |0 Cx -Sx|*| 0 1 0|*|Sz Cz 0| = | SxSyCz+CxSz -SxSySz+CxCz -SxCy| // |0 Sx Cx| |-Sy 0 Cy| | 0 0 1| |-CxSyCz+SxSz CxSySz+SxCz CxCy| /////////////////////////////////////////////////////////////////////////////// void anglesToAxes(const Vector3 angles, Vector3& left, Vector3& up, Vector3& forward) { const float DEG2RAD = 3.141593f / 180; float sx, sy, sz, cx, cy, cz, theta; // 绕X轴旋转 (pitch) theta = angles.x * DEG2RAD; sx = sinf(theta); cx = cosf(theta); // 绕Y轴旋转 (yaw) theta = angles.y * DEG2RAD; sy = sinf(theta); cy = cosf(theta); // 绕Z轴旋转 (roll) theta = angles.z * DEG2RAD; sz = sinf(theta); cz = cosf(theta); // 确定左轴 left.x = cy*cz; left.y = sx*sy*cz + cx*sz; left.z = -cx*sy*cz + sx*sz; // 确定上轴 up.x = -cy*sz; up.y = -sx*sy*sz + cx*cz; up.z = cx*sy*sz + sx*cz; // 确定前轴 forward.x = sy; forward.y = -sx*cy; forward.z = cx*cy; }