你的问题不太对。不要问“一旦你旋转了某个东西,轴就不再是相同的(x,y),那么你怎么知道在哪个轴上旋转了多少?”,问“我怎样才能围绕全局轴而不是局部轴旋转轴?”。您希望围绕全局 x 的旋转始终是围绕全局 x 的旋转,而不是围绕对象的局部 x 轴的旋转。您不会根据当前状态进行不同的旋转,您只需更改您在概念上旋转的内容。
首先,您可能正在存储 rotX、rotY 和 rotZ 并调用 glRotatef 来应用旋转?这对于你想做的事情来说还不够好。您正在做的是用欧拉角描述方向。您总是会遇到gimbal lock,这是其中一个轴的某些值将其他两个轴变成同一轴的地方。所以你失去了一定程度的自由。查看文章以获得正确的解释。
因为作为 OpenGL 编码器,您会对它们非常满意,假设您将对象方向直接存储为称为 M 的矩阵。您将旋转存储为您应用以实现旋转的矩阵。没有明确的角度或轴概念。
假设你想绕 x 旋转。您围绕 x 轴生成一个合适的旋转矩阵,称为 R。这样的矩阵将是 OpenGL 在内部构建以执行 glRotatef(angle, 1, 0, 0) 的东西。
那么你实际上有两个选择——用 MR 替换 M 或用 RM 替换 M。矩阵乘法不是可交换的,所以它们不一样。就像使用 OpenGL 矩阵堆栈将 6 向左平移然后旋转 40 度产生的结果与旋转 40 度然后将 6 向左平移产生不同的结果。
在这种情况下,R 是对象,所以 MR 是后乘法,RM 是预乘法。矩阵的乘法创建一个矩阵,当应用于向量时,与应用第二个然后应用第一个具有相同的效果。因此,如果您进行后乘,您会得到结果,就好像您将对象旋转了新的旋转,然后是它之前的所有对象。这显然是错误的方法。您希望新的修改在迄今为止的所有其他修改之后生效。所以你预乘。然后将结果存储为新的 M。
实际上,仅此而已。您可以显式地使用 GL 矩阵堆栈快速进行概念验证,例如:
// ... I assume the modelview stack is active, M is an array of 16 floats ...
// ensure whatever's on the stack currently is kept safe
glPushMatrix();
// but we don't actually care what it was, so load the identity
glLoadIdentity();
// build our rotation matrix first
glRotatef(changeAroundX, 1, 0, 0);
glRotatef(changeAroundY, 0, 1, 0);
// multiply by M. OpenGL postmultiplies by the newer matrix, so this
// is premultiplying M by whatever we just loaded
glMultMatrixf(M);
// read the new M back
glGetFloatv(GL_MODELVIEW_MATRIX, M);
// and pretend we never touched the stack
glPopMatrix();
然后使用 glMultMatrixf(M) 代替 glRotatef 来应用当前对象旋转。这只是一个概念证明,因为你很快就会在 M 中得到一些令人讨厌的数字错误,这只是你对它进行大量操作的结果。您可以随时修复 M,但如果您已经拥有四元数代码并且对它们感到满意,那么沿着这条路线走可能会更聪明。只需用等价的四元数替换逻辑矩阵运算。