How do glPushMatrix() and glPopMatrix() keep the scene the same?

23,401

Solution 1

It's only the matrix, used to transform coordinates, that is restored by glPopMatrix. Not the whole framebuffer. The framebuffer contains the rendering of the quadrilateral, changing the matrix afterward doesn't affect anything already rendered.

Solution 2

glPushMatrix duplicates the matrix on top of the stack (you're always working with the top one). Any other transformation you are doing modifies this top matrix, the duplicated one. When you do glPopMatrix we are back to the original matrix.

For example, suppose you want to draw a car. You set up the matrix to draw the body of the car, let's call it M1. Now, you want to draw one wheel. You can either compute M2 - the matrix needed for the wheel to be displayed correctly - or, since the wheel is relative to the body of the car (thus, there is a matrix M3 such that M2 = M1 * M3) you modify M1. But the car has 4 wheels, you need to keep a copy of M1. You do this by doing a glPushMatrix, you get back the copy by doing a glPopMatrix.

glPushMatrix and glPopMatrix

When you draw anything on the screen, you are giving coordinates in object space. To really display something, those coordinates need to be transformed. For that we have some matrices.

In the wheel example, you have only one wheel geometry but because you are using different matrices, there will be four wheels drawn. glPushMatrix and glPopMatrix work only with the matrix, the actual vertex data is kept into the GPU, each glVertex sends another one there and it cannot be removed. See the following image, the matrices are used only for transforming object coordinates to world coordinates (actually, all matrices can be pushed into a stack)

multiple matrices

Solution 3

OpenGL is a drawing API. When you call drawing functions, things are literally drawn to the framebuffer the very moment you issue the draw call -- well actually OpenGL batches up all the commands internally and processes them in order. But when OpenGL is about to process those drawing calls, it will draw to the framebuffer with the matrices set to the state at this specific position in the batch.

So to reemphase this: OpenGL does not do any kind of scene management, it just draws things to the framebuffer in the order and way you issue the drawing commands. You send a triangle: OpenGL will transform and draw it. There's no scene internally built. Once you understood this it becomes trivial to understand how the matrix stack can do its "magic".

Share:
23,401
drjrm3
Author by

drjrm3

BS - Mathematics MS - Mathematics MS - Computer Science PhD - Computational Science

Updated on October 16, 2020

Comments

  • drjrm3
    drjrm3 over 3 years

    I found some code online which will move a box across the screen, then reset it after the box hits the end of the screen.
    Here is the code:

    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();
    }
    

    Now, the way I understand glPushMatrix() and glPopMatrix() is that glPushMatrix() puts (or pushes) a new matrix on the stack for you to do things to, so that after you pop it back off you have a "clean" slate again. This is why, if I neglect the glPopMatrix() after glEnd(), my square seems to accelerate rather than move at a constant velocity.

    How is it, however, that the changes I make inside of glPushMatrix() and glPopMatrix() are kept? When I use glPushMatrix() and make a change to the top matrix, it visualizes the changes, but when i use glPopMatrix(), aren't all those changes gone? When I am restored to a "clean" slate again, how is it that my box moves across the screen?

    How is the state of that translation recorded if i just pop the matrix off again after making the change?

  • drjrm3
    drjrm3 almost 13 years
    so the matrix affects the framebuffer each time? thus, when i push a matrix it changes the frame buffer, but when i pop it back, the framebuffer is already changed so it doesn't matter if i have the identity as my matrix now?
  • drjrm3
    drjrm3 almost 13 years
    thank you, i have a better understanding of how glPushMatrix() and glPopMatrix() work now, but i guess my question is - how is it that the wheels (in your example) would remain in the picture if we popped back the matrix to get M1? in my code, i push, translate, then pop ... but all previous translations have been recorded (as the box is moving across the screen). it seems to me that if i push, translate, then pop - then i am back to the original matrix (and thus, nothing should have happened).
  • Ben Voigt
    Ben Voigt almost 13 years
    @Laurbert: glPushMatrix doesn't change anything, it just saves a copy of the current matrix so it can be recalled later. When you render geometry, the current matrix is used, and pixel values are changed in the framebuffer. If the matrix later changes for any reason, it doesn't affect what's already been drawn.
  • Christian Rau
    Christian Rau almost 13 years
    @Laurbert515 You constantly increase your box translation with the elapsed time, so why should it not move over the screen? You should also keep in mind, that OpenGL does not know anything of any objects. It just draws the vertices the moment you make the draw calls (glVertex in your case) and then forgets about them. So you can just change the matrix transformation and draw some other things.
  • Christian Rau
    Christian Rau almost 13 years
    +1 For once again a completely clear "it's a drawing API"-answer. It's a pity every second answer from you has to be of this kind. Maybe all those tutorials and books out there should state this more clearly.