【问题标题】:OpenGL transformations not working as expectedOpenGL 转换未按预期工作
【发布时间】:2014-11-28 04:00:05
【问题描述】:

我正在编写一个 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();

【问题讨论】:

    标签: c# opengl opentk


    【解决方案1】:

    您的第二次尝试走在了正确的轨道上,但它仍然遗漏了几个方面:

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

    代码序列将如下所示:

    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() 放在结尾。这实际上更具可扩展性,因为它会在需要时恢复矩阵以进行额外渲染。

    我在这里对类似问题的回答中更详细地解释了其中的一些:Rotating an object around a fixed point using glMultMatrix

    【讨论】:

    • 成功了,谢谢!但是,为什么这些操作以相反的顺序执行?这仅仅是因为矩阵乘法的工作原理吗?我必须承认,从编程的角度来看,必须反转操作流程似乎违反直觉。
    • 我也想知道:为什么PushMatrixPopMatrix没有效果?无论我是否将代码包装在其中,结果都是一样的。
    • 我添加了一个关于PushMatrixPopMatrix 的部分。转换顺序问题是一个更大的话题。是的,这与矩阵的相乘方式有关。在大多数情况下,这种行为也是有意义的。例如,它允许您先指定视图转换,然后开始绘制对象,同时为每个对象指定模型转换。然后将首先应用模型转换,然后是视图转换,这是它需要的方式。如果您在每个级别都有具有相对变换的对象层次结构,它会变得更加有用。
    • @ReticulatedSpline:是的,这就是 OpenGL 使用的矩阵语义的工作方式。这个想法是,您可以通过在末尾“添加”轻松构建转换层次结构。为了实现这一点,OpenGL 使用与右侧矩阵相乘的列向量。如果一切都被调换了,那么顺序就会相反。然而,对于初学者来说,这可能是多么违反直觉,以这种方式对矩阵应用程序进行排序是非常有益的,并且可以简化很多事情。
    猜你喜欢
    • 1970-01-01
    • 2016-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多