【问题标题】:How do glPushMatrix() and glPopMatrix() keep the scene the same?glPushMatrix() 和 glPopMatrix() 如何保持场景不变?
【发布时间】:2011-08-11 19:40:14
【问题描述】:

我在网上找到了一些代码,它可以在屏幕上移动一个框,然后在框到达屏幕末尾后重置它。
代码如下:

void display(void) {
  int sign = 1;
  if (lastFrameTime == 0) {
    /*
     * sets lastFrameTime to be the number of milliseconds since
     * Init() was called;
     */
    lastFrameTime = glutGet(GLUT_ELAPSED_TIME);
  }

  int now = glutGet(GLUT_ELAPSED_TIME);
  int elapsedMilliseconds = now - lastFrameTime;
  float elapsedTime = float(elapsedMilliseconds) / 1000.0f;
  lastFrameTime = now;

  int windowWidth = glutGet(GLUT_WINDOW_WIDTH);

  if (boxX > windowWidth) {
    boxX -= windowWidth;
  }
  boxX += (sign)*256.0f * elapsedTime;

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glPushMatrix();
  //creates a new matrix at the top that we can do things to?
  glTranslatef(boxX, 0.0f, 0.0f);

  /*
   * draw a "quad" (rectangle)
   */
  glBegin(GL_QUADS);
  glVertex2f(0.0f, 0.0f);
  glVertex2f(128.0f, 0.0f);
  glVertex2f(128.0f, 128.0f);
  glVertex2f(0.0f, 128.0f);
  glEnd();
  glPopMatrix();
  //pops that matrix off the stack so we can have a "clean" version to do something next time.?

  glutSwapBuffers();
}

现在,我理解 glPushMatrix() 和 glPopMatrix() 的方式是 glPushMatrix() 在堆栈上放置(或推送)一个新矩阵供您执行操作,以便在您将其弹出后您有一个再次“干净”的石板。这就是为什么如果我在glEnd() 之后忽略glPopMatrix(),我的方块似乎会加速而不是匀速移动。

但是,我在glPushMatrix() 和glPopMatrix() 中所做的更改是如何保留的?当我使用glPushMatrix() 并更改顶部矩阵时,它会显示更改,但是当我使用glPopMatrix() 时,所有这些更改不是都消失了吗?当我再次恢复到“干净”的状态时,我的盒子是如何在屏幕上移动的?

如果我在进行更改后再次弹出矩阵,该翻译的状态如何记录?

【问题讨论】:

  • 顺便说一句。你知道,你是通过阅读贬值的代码来学习 OpenGL 的吗?因为 PushMatrix 和 PopMatrix 在 OpenGL 3.0 中已被弃用。 opengl.org/registry/doc/glspec30.20080923.pdf(406 页)您的绘画方式(开始;顶点;结束)也已被弃用。这实际上是最糟糕的方法。所以,我只是指出问题,但我没有很好的解决方案。尝试用谷歌搜索 openGL 3.1 教程。您甚至可以尝试找到好的 OpenGL ES 教程。如果您刚开始学习 OpenGL,请尝试学习干净的 OpenGL 方法。
  • @petraszd:问题是仍然有很多资源使用旧样式教授 OpenGL。在 NeHe 等资源升级之前,我们仍然需要为这些问题提供解决方案。
  • @Mihai:NeHe 几乎已经死了(没有更新),所以他们不会升级。
  • 那么有人必须创建一个等价物:)
  • @petraszd:如果有人难以理解固定功能管道,您希望他们如何在着色器中重新实现他们需要的行为?无论哪种情况,他们都需要了解转换矩阵。

标签: c++ opengl graphics


【解决方案1】:

glPushMatrix 复制堆栈顶部的矩阵(您总是使用顶部的矩阵)。您正在进行的任何其他转换都会修改此顶部矩阵,即重复的矩阵。当您执行glPopMatrix 时,我们将返回原始矩阵。

例如,假设您想画一辆汽车。你设置了矩阵来绘制车身,我们称之为M1。现在,你想画一个轮子。您可以计算 M2 - 正确显示车轮所需的矩阵 - 或者,由于车轮是相对于车身的(因此,有一个矩阵 M3 使得 M2 = M1 * M3)您修改M1。但是这辆车有4个轮子,你需要保留一份M1。您可以通过 glPushMatrix 执行此操作,然后通过 glPopMatrix 获取副本。

当你在屏幕上绘制任何东西时,你就是在对象空间中给出坐标。要真正显示某些东西,需要转换这些坐标。为此,我们有一些矩阵。

在车轮示例中,您只有一个车轮几何形状,但由于您使用不同的矩阵,因此将绘制四个车轮。 glPushMatrix 和 glPopMatrix 仅与矩阵一起工作,实际的顶点数据保存在 GPU 中,每个 glVertex 发送另一个在那里并且无法删除。见下图,矩阵仅用于将物体坐标转换为世界坐标(实际上,所有矩阵都可以压入堆栈)

【讨论】:

  • 谢谢,我现在对 glPushMatrix() 和 glPopMatrix() 的工作原理有了更好的理解,但我想我的问题是 - 轮子(在你的例子中)怎么会留在图片如果我们弹出矩阵来得到 M1?在我的代码中,我推送、翻译、然后弹出……但所有以前的翻译都已记录(当框在屏幕上移动时)。在我看来,如果我推动、翻译、然后弹出 - 然后我回到原始矩阵(因此,什么都不应该发生)。
  • @Laurbert515 你会随着时间的推移不断增加你的盒子翻译,那么为什么它不应该在屏幕上移动呢?您还应该记住,OpenGL 不知道任何对象的任何内容。它只是在您进行绘图调用(在您的情况下为glVertex)时绘制顶点,然后忘记它们。所以你可以只改变矩阵变换并画一些其他的东西。
【解决方案2】:

OpenGL 是一个绘图 API。当你调用绘图函数时,在你发出绘图调用的那一刻,事情就被真正地绘制到了帧缓冲区——实际上,OpenGL 在内部对所有命令进行批处理并按顺序处理它们。但是当 OpenGL 即将处理这些绘图调用时,它将绘制到帧缓冲区,其中矩阵设置为批处理中此特定位置的状态。

所以要再次强调这一点:OpenGL 不做任何类型的场景管理,它只是按照您发出绘图命令的顺序和方式将内容绘制到帧缓冲区。您发送一个三角形:OpenGL 将转换并绘制它。没有内部构建的场景。一旦你理解了这一点,理解矩阵堆栈如何发挥它的“魔力”就变得微不足道了。

【讨论】:

  • +1 再次得到一个完全清楚的“这是一个绘图 API”的答案。很遗憾,你的每一秒回答都必须是这样的。也许所有那些教程和书籍都应该更清楚地说明这一点。
【解决方案3】:

glPopMatrix 恢复的只是用于变换坐标的矩阵。不是整个帧缓冲区。帧缓冲区包含四边形的渲染,之后更改矩阵不会影响已渲染的任何内容。

【讨论】:

  • 所以矩阵每次都会影响帧缓冲区?因此,当我推送一个矩阵时,它会更改帧缓冲区,但是当我将其弹出时,帧缓冲区已经更改,所以我现在是否将身份作为我的矩阵并不重要?
  • @Laurbert: glPushMatrix 不会改变任何东西,它只是保存当前矩阵的副本,以便以后调用。渲染几何图形时,将使用当前矩阵,并在帧缓冲区中更改像素值。如果矩阵后来因任何原因发生变化,它不会影响已经绘制的内容。
猜你喜欢
  • 1970-01-01
  • 2021-06-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多