在大多数3D平台游戏中,只需围绕Y轴旋转,因为玩家总是直立。
但是,对于需要在所有轴上旋转玩家的3D太空游戏,表示旋转的最佳方式是什么?
我首先尝试使用欧拉角:
glRotatef(anglex, 1.0f, 0.0f, 0.0f);
glRotatef(angley, 0.0f, 1.0f, 0.0f);
glRotatef(anglez, 0.0f, 0.0f, 1.0f);
我采用这种方法的问题是每次旋转后,轴都会发生变化。例如,当anglex和angley为0时,anglez使船绕其机翼旋转,但是如果anglex或angley不为零,则不再如此。我希望anglez始终围绕翅膀旋转,与anglex和angley无关。
我读到四元数可以用来展示这种期望的行为,但是在实践中无法实现它。
我认为我的问题是由于我基本上仍在使用欧拉角,但在使用前将旋转转换为四元数表示。
struct quaternion q = eulerToQuaternion(anglex, angley, anglez);
struct matrix m = quaternionToMatrix(q);
glMultMatrix(&m);
但是,如果直接存储每个X,Y和Z角度不正确,当我的旋转存储为四元数时,如何说“将机翼(或任何一致轴)绕船旋转1度”? / p>
此外,我希望能够以旋转的角度转换模型。假设我只有q.x,q.y,q.z和q.w的四元数,我该如何移动它?
答案 0 :(得分:2)
四元数是表示旋转的非常好的方法,因为它们是有效的,但我更喜欢用4x4矩阵表示完整状态“位置和方向”。
因此,假设您为场景中的每个对象都有一个4x4矩阵。最初,当对象未旋转且未经过处理时,此矩阵是单位矩阵,这就是我将其称为“原始状态”。例如,假设您的船头指向原始状态的-z,因此沿z轴旋转船舶的旋转矩阵为:
Matrix4 around_z(radian angle) {
c = cos(angle);
s = sin(angle);
return Matrix4(c, -s, 0, 0,
s, c, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
}
现在,如果你的船在太空中的任何地方并且向任何方向旋转,并且让我们调用此状态t
,如果你想围绕z轴旋转angle
量的船,就好像它一样处于“原始状态”,它将是:
t = t * around_z(angle);
使用OpenGL进行绘制时,t
是您为该船的每个顶点相乘的内容。这假设你正在使用列向量(就像OpenGL那样),并且要注意OpenGL中的矩阵首先是存储列。
基本上,您的问题似乎与您应用旋转的顺序有关。参见,四元数和矩阵乘法是非交换的。所以,如果相反,你写:
t = around_z(angle) * t;
您将around_z
旋转不应用于“原始状态”z,而是应用于全局坐标z,船舶已受初始转换影响(已转换和已翻译) 。当您调用glRotate
和glTranslate
函数时,这是相同的。他们被称为重要的顺序。
针对您的问题稍微具体一点:您拥有绝对翻译trans
,以及围绕其中心rot
的轮播。您可以使用以下内容更新场景中的每个对象:
void update(quaternion delta_rot, vector delta_trans) {
rot = rot * delta_rot;
trans = trans + rot.apply(delta_trans);
}
delta_rot
和delta_trans
都以相对于原始状态的坐标表示,所以,如果你想推进你的船只前进0.5个单位,你的delta_trans
将是(0, 0,-0.5)。要绘制,它将类似于:
void draw() {
// Apply the absolute translation first
glLoadIdentity();
glTranslatevf(&trans);
// Apply the absolute rotation last
struct matrix m = quaternionToMatrix(q);
glMultMatrix(&m);
// This sequence is equivalent to:
// final_vertex_position = translation_matrix * rotation_matrix * vertex;
// ... draw stuff
}
通过阅读glTranslate
和glMultMatrix
的手册,我选择的通话顺序,以保证应用转换的顺序。
关于rot.apply()
正如维基百科文章Quaternions and spatial rotation所述,要在向量q
上应用四元数p
描述的轮播,它将是rp = q * p * q^(-1)
,其中rp
是新旋转的矢量。如果你在游戏中实现了一个有效的四元数库,你应该已经实现了这个操作,或者现在应该实现它,因为这是使用四元数作为旋转的核心。
例如,如果你有一个描述90°(0,0,1)左右旋转的四元数,如果你将它应用于(1,0,0),你将得到向量(0,1, 0),即你有四元数旋转的原始矢量。这相当于将四元数转换为矩阵,并将矩阵转换为柱形矢量乘法(通过矩阵乘法规则,它产生另一个列矢量,旋转矢量)。