openGL旋转重置

openGL rotation reset

本文关键字:旋转 openGL      更新时间:2023-10-16

我当前正在查看一组球体,并在分别按下按钮x、y或z时围绕x、y、z旋转它们。旋转有效。但当我用I重置我的旋转时,我并不总是能得到完美的重置。

case 'x':
case 'X':
glMatrixMode(GL_MODELVIEW);
glutSetWindow(windowID[0]);
glRotatef(1, 1, 0, 0);
glutPostRedisplay();
xRotation++;
break;
case 'y':
case 'Y':
glMatrixMode(GL_MODELVIEW);
glutSetWindow(windowID[0]);
glRotatef(1, 0, 1, 0);
glutPostRedisplay();
yRotation++;
break;
case 'z':
case 'Z':
glMatrixMode(GL_MODELVIEW);
glutSetWindow(windowID[0]);
glRotatef(1, 0, 0, 1);
glutPostRedisplay();
zRotation++;
break;
case 'i':
case 'I':
glMatrixMode(GL_MODELVIEW);
glutSetWindow(windowID[0]);
glRotatef(-xRotation, 1, 0, 0);
glRotatef(-yRotation, 0, 1, 0);
glRotatef(-zRotation, 0, 0, 1);
glutPostRedisplay();

旋转非常有效。每次增量,我都会将增量添加到各自的浮动(x\y\z)旋转中,以存储我绕轴进行的增量,然后使用这些增量重置回原点。

我发现我的问题是当我沿着2个或多个轴旋转时。我似乎一次只在一个轴上遇到这个问题。

每个窗口都用绘制

void DrawWindowOne()
{
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, 250, 250);
DrawAxis(1);
glMatrixMode(GL_MODELVIEW);
DrawSpheres();
glViewport(250, 0, 250, 250);
DrawAxis(1);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT1);
DrawSpheres();
glDisable(GL_LIGHT1);
glViewport(500, 0, 250, 250);
DrawAxis(1);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_LIGHT2);
DrawSpheres();
glDisable(GL_LIGHT2);
glDisable(GL_LIGHTING);
glutSwapBuffers();
}

samgak为回答您的问题所说的话会起作用,但这是需要注意的,并且很好地了解这一点,以防止以后项目变得庞大和复杂时出现陷阱。

当在三维空间中进行旋转并需要绕任何给定的轴[X,Y,Z]旋转时,在平移-旋转矩阵中,旋转是使用欧拉角完成的,会出现一个已知的问题。这个问题被称为万向锁。以你的球体为例,一个你想绕任何轴旋转的三维模型;在你开始旋转之前,想象有一个比你的球体大的圆或环,并平行于每个轴放置。我们可以说,我们的X轴环有一个指向右或东的手柄,你的Y轴环有指向上或北的手柄,而你的Z轴环指向你。当你抓住一个手柄并开始移动它时,这个环将在该轴上旋转对象。

想象一下你的球体是地球:

  • X轴:当你旋转你的3D模型时,这将使它绕着X轴东旋转;西方和地球将与北方一起转动;南极和赤道改变方向。如果你向下看Z轴(LookAt),赤道将向你的倾斜或旋转

  • Y轴当您旋转三维模型时,这将使其绕y轴北&南方和地球将像地球日一样围绕两极旋转。赤道不会移动。

  • Z轴当你旋转你的三维模型时,这将使它围绕Z轴"LookAt"旋转(仍然是水平的,但垂直于东和西),地球将围绕LookAt轴和北和西旋转;南极&赤道将改变方向。沿着LookAt轴往下看,你会看到两极和赤道向东和向西倾斜。

这里有一个类比来证明这一点,就像在环的运动中旋转你的3D模型一样:我将使用摩托车,把轴的旋转想象成下面的

  • X轴-自行车油门
  • Y轴-转动手柄杆
  • Z轴-向左倾斜自行车&对

注意:当您绕任何轴旋转对象时,环会随着显示旋转的对象一起移动。

现在你已经看到了每一次旋转发生的事情,我将展示Euler Angels的Gimbal Lock。我们将不使用Y轴,在这里使用X轴或Z轴也没关系。我将使用Z轴,因为我们向下看,当你旋转物体时,你可以看到它左右旋转。让我们将这个物体向任意一个方向(CW,CCW)旋转90度或PI/2,现在我们的Z轴环与该物体一起旋转,由于我们使用地球,我们仍然可以看到我们面前的同一片土地,但赤道现在是两极所在的地方,两极现在是赤道所在的地方。我们没有接触其他两个轴,也没有旋转。但如果您注意到,您将看到Z轴环现在与X轴对齐。现在,如果我们开始绕X轴旋转,Z轴也随之旋转。我们已经失去了旋转的自由度。这就是术语Gimbal Lock所指的。

如果我们重新设置所有内容,这可能发生在3个轴之间的6种不同组合中。因此,无论你尝试以哪种方式在所有三维中自由旋转,只使用3D数学或使用欧拉角的3D旋转矩阵;这是使用正弦和余弦函数绕每个轴旋转的基本矩阵,你总是会遇到Gimbal Lock的情况。

有一种方法可以避免这种情况!使用四元数!对他们来说,数学有点难理解,因为它是一种4D矢量类型,它确实涉及虚平面和复数,但它们的编程相当容易,因为它们非常类似于矢量和矩阵。有了这个第四个零部件,就可以围绕(X,Y,Z)自由旋转,而不会出现这个问题。Gimbal Lock和四元数上有大量的资源用于数学和它们的编程,并且已经存在许多不错的数学库,它们已经为您定义了四元数,只需阅读它们关于如何在代码中应用它们的文档即可。它们让生活变得轻松多了。

需要注意的一点是,在三维空间中旋转时不必总是使用四元数,这始终取决于需要。以下是一些使用四元数更好的示例,以及可以使用欧拉角的示例。

欧拉角:如果你知道你将只在一个轴上旋转。

  • 桶在地上滚动
  • 孩子的秋千
  • 拉锯
  • 摇椅

四元数旋转:当您需要2个或多个轴的自由度时。

  • 相机旋转
  • 在地上滚动的球
  • 飞行的飞机或喷气式飞机
  • 在太空中移动的航天飞机或卫星
  • 潜艇
  • 字符AI-当身高是一个因素时的检测和跟踪算法
  • 当高度也是一个因素时的任何角色移动。鸟飞,鱼游,角色动画-布娃娃物理

这里有一个描述Gimbal Lock的视频链接,这是一个非常精确和详细的视图,可以在不到10分钟的时间内完全理解这一现象。YouTube:Gimbal Lock

您可以通过调用重置矩阵

glLoadIdentity();

而不是这个代码:

glRotatef(-xRotation, 1, 0, 0);
glRotatef(-yRotation, 0, 1, 0);
glRotatef(-zRotation, 0, 0, 1);

你遇到的问题是矩阵旋转是不可传递的。围绕x然后y旋转与围绕y然后x旋转不同。如果允许用户使用键围绕每个轴进行多次交互式增量旋转,则不可能简单地将角度累积在3个变量中,并通过围绕每个轴旋转负值来撤消旋转。您必须存储旋转的顺序以及每次旋转的次数。因此,将矩阵重置为单位要容易得多。

不要在模型视图矩阵中重复调用Rotate(1,1,0,0)来累积旋转,而是在按下键时增加变量,然后在每次更新设置一次单位矩阵后使用Rotate(angle_x,1,0,0)。否则,随着时间的推移,你会得到矩阵漂移,小的舍入误差会导致你的旋转矩阵退化。

重置对象只意味着设置angle_x=0.0f;

也不要把你的观察矩阵放在投影矩阵中,因为这会导致意想不到的结果。看见http://sjbaker.org/steve/omniv/projection_abuse.html以获得完整的描述。

通常在主循环之前设置一次投影矩阵。

然后在每个循环中将当前矩阵设置为modelview。然后,对于要绘制的每个对象,将modelview矩阵设置为lookat*model。

我还建议阅读opengl如何使用这里的各种矩阵