OpenGL 转换未按预期工作

本文关键字:工作 转换 OpenGL | 更新日期: 2023-09-27 18:32:22

我正在编写一个OpenGL应用程序(C#和OpenTK),我对OpenGL转换的工作原理感到困惑。

我已经设置了一个透视投影,我位于默认位置,默认方向(+X向右,+Y向上,+Z向我袭来。现在我在 XY 平面上画了一个四边形,在 Z 轴上为 -10。

GL.Begin(PrimitiveType.Quads);
GL.Color3(Color.Green);
GL.Vertex3(-1, 1, -10);
GL.Vertex3(1, 1, -10);
GL.Vertex3(1, -1, -10);
GL.Vertex3(-1, -1, -10);
GL.End();

这按预期工作。但是现在我想沿其本地 Y 轴旋转四边形,所以我添加了旋转,它正在应用于我的单位矩阵。下面是相关的代码部分:

GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();
GL.Rotate(10, 0, 1, 0);
GL.Begin(PrimitiveType.Quads);
GL.Color3(Color.Green);
GL.Vertex3(-1, 1, -10);
GL.Vertex3(1, 1, -10);
GL.Vertex3(1, -1, -10);
GL.Vertex3(-1, -1, -10);
GL.End();

但这会使飞机围绕我的"世界"Y 轴旋转,所以现在飞机不再通过我的 -Z 轴,而是成一定角度。

问题

如何将对象保持在所需位置(沿 Z 轴为 -10),但让它绕自己的轴旋转?

我尝试过什么

尝试先平移到原点,执行旋转,绘图,然后向后移动,但这也不起作用,大概是因为一旦我旋转了,现在我不再沿着"世界"轴平移,而是沿着旋转轴。这是代码:

GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();
GL.Translate(0, 0, 10);
GL.Rotate(10, 0, 1, 0);
GL.Begin(PrimitiveType.Quads);
GL.Color3(Color.Green);
GL.Vertex3(-1, 1, -10);
GL.Vertex3(1, 1, -10);
GL.Vertex3(1, -1, -10);
GL.Vertex3(-1, -1, -10);
GL.End();
GL.Translate(0, 0, -10);

我有一种感觉,我需要使用PushMatrixPopMatrix,但我不太确定我是否理解它们是如何发挥作用的。如果我在执行任何操作之前推送我的矩阵,然后弹出它,我的视图不应该恢复正常吗?如果是这样,为什么这不起作用:

GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();
// Push current matrix onto the stack
GL.PushMatrix();
// Perform operations on newly pushed matrix
GL.Translate(0, 0, 10);
GL.Rotate(10, 0, 1, 0);
GL.Begin(PrimitiveType.Quads);
GL.Color3(Color.Green);
GL.Vertex3(-1, 1, -10);
GL.Vertex3(1, 1, -10);
GL.Vertex3(1, -1, -10);
GL.Vertex3(-1, -1, -10);
GL.End();
// Pop it off, so return to my previous matrix
GL.PopMatrix();

OpenGL 转换未按预期工作

你的第二次尝试是正确的,但它仍然错过了几个方面:

  • 开始绘制之前,需要指定所有转换。只有在进行绘制调用时处于活动状态的转换才会应用于该绘制调用中的坐标。glEnd()之后进行翻译将无济于事。
  • 顺序是倒退的。最后指定的变换首先应用于顶点。因此,需要最后指定将几何图形移动到原点的平移,并且需要首先指定将其移回其位置的平移。

然后,代码序列将如下所示:

GL.Translate(0, 0, -10);
GL.Rotate(10, 0, 1, 0);
GL.Translate(0, 0, 10);
GL.Begin(PrimitiveType.Quads);
...
GL.End();

此代码示例中不需要PushMatrix()PopMatrix(),因为在绘制函数开始时调用LoadIdentity(),这会将当前转换还原为标识转换。至少根据显示的内容,您在此代码之后不会绘制任何内容,因此无需还原以前的矩阵。

另一种方法是不为每个帧调用LoadIdentity(),而是将上面的代码段括起来,开头用PushMatrix(),结尾用PopMatrix()。这实际上更具可扩展性,因为它可以在需要时恢复矩阵以进行其他渲染。

我在回答一个类似的问题时更详细地解释了其中的一些内容:使用 glMultMatrix 围绕固定点旋转对象。