PDA

View Full Version : I on't understand this simple matrix stack example

Karottensuppe
06-05-2009, 12:34 PM
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glMatrixMode(GL_MODELVIEW);

glTranslatef(0, 0, -50);

glRotatef(xRotate, 1.0f, 0.0f, 0.0f);
glRotatef(yRotate, 0.0f, 1.0f, 0.0f);
glRotatef(zRotate, 0.0f, 0.0f, 1.0f);

//bind texture

glColor3f(255,255,0);
glVertex3f(-10.0f, -10.0f, 10.0f);
glVertex3f(10.0f, -10.0f, 10.0f);
glVertex3f(10.0f, 10.0f, 10.0f);
glVertex3f(-10.0f, 10.0f, 10.0f);
glEnd();

glPushMatrix();

glTranslatef(10,10,0);
glColor3f(255,0,0);
glVertex3f(-10.0f, -10.0f, 10.0f);
glVertex3f(10.0f, -10.0f, 10.0f);
glVertex3f(10.0f, 10.0f, 10.0f);
glVertex3f(-10.0f, 10.0f, 10.0f);
glEnd();

glPopMatrix();

glutSwapBuffers();

In the pushMatrix() popMatrix() block, I dont understand why i have to put the translatef() command BEFORE the vertex declarations to have the quad move. Doesn't pushMatrix() force all items in the block be pushed upon a stack? Meaning that I need to translate the coordinate system first, before drawing the quad?
Also: why does the yellow quad lie BEFORE the red one? I have no pushmatrix() around the two quads.

dletozeun
06-05-2009, 03:54 PM
As its name says, glPushMatrix pushes current matrix on the matrix stack depending on the current matrix mode set. The rest of the data like vertices definitions are not pushed in this stack as you seem to say.

http://www.opengl.org/sdk/docs/man/

Moreover, you have to set the transformation before drawing stuff, that is why you put the glTranslatef call before specifying vertex data. Basically, all vertices defined after the translatef call will be affected by this transformation unless you revert it.

why does the yellow quad lie BEFORE the red one?

Nothing to do with glPush/glPop, see the "depth buffer" technique. You really need to start reading a tutorial about opengl to understand the basics of computer graphics, then this stuff will be clearer to you.

One last thing, the glPush/glPop are useless in the code above, you can remove them and you won't see any change since you are not apparently drawing anything more after this block.

dorbie
06-05-2009, 11:17 PM
In this example the pushmatrix and popmatrix are redundant because nothing is drawn with teh restored matrix after the pop.

If the application becomes more complex it may be useful

Typically the viewing transformation is applied to the modelview matrix and this positions the eye relative to the world. Next as objects are drawn they have their own transformation to position them in the world and these are applied for each object using the push and pop calls to restore the basic viewing transformation.

Objects drawn with only the viewing transformation have their object space the same as their world space. i.e. they are relative to the global origin rather than some local origin under transformation.

Anything drawn with identity on the stack has coordinates in eyespace.

dorbie
06-05-2009, 11:34 PM
Here is the commented code:

// clear the screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// ensure matrix manipulations are applied to the matrix reserved for transformations from object space to eye space prior to projection
glMatrixMode(GL_MODELVIEW);

// initialize the matrix so it has no effect on vertices and the previous frames settings are reset

// Apply a viewing transformation to translate the eye along the Z axis (in this case -50 means the eye will be at +50 since this moves vertices bt -50

glTranslatef(0, 0, -50);

// orient the eye to look in some direction determined by the Euler rotations series provided
// Note that the order of these transformations (translate then rotate) rotates the translated eye around the origin rather than translates independent of the rotation.
glRotatef(xRotate, 1.0f, 0.0f, 0.0f);
glRotatef(yRotate, 0.0f, 1.0f, 0.0f);
glRotatef(zRotate, 0.0f, 0.0f, 1.0f);

//bind texture

// Draw a yellow quad near the 'world' origin but +10 in z position.

glColor3f(255,255,0);
glVertex3f(-10.0f, -10.0f, 10.0f);
glVertex3f(10.0f, -10.0f, 10.0f);
glVertex3f(10.0f, 10.0f, 10.0f);
glVertex3f(-10.0f, 10.0f, 10.0f);
glEnd();

// Preserve the viewing transformation for later restoration
glPushMatrix();

// Translate an object relative to the world origin to +10 x and +10 y, this is a model transformation ans so intuitively you're translating the object by the values supplied
glTranslatef(10,10,0);

// draw a red quad also around the origin but +10 in z, this quad being drawn after the above translation will appear to the upper right of the yellow quad but it will overlap with the yellow quad in it's lower left quadrant. (upper left and lower right assumes no rotation in teh viewing transform).

glColor3f(255,0,0);
glVertex3f(-10.0f, -10.0f, 10.0f);
glVertex3f(10.0f, -10.0f, 10.0f);
glVertex3f(10.0f, 10.0f, 10.0f);
glVertex3f(-10.0f, 10.0f, 10.0f);
glEnd();

// Undo the previous translation by restoring the viewing transformation to the modelview matrix.

glPopMatrix();

// display the results
glutSwapBuffers();

Karottensuppe
06-06-2009, 04:04 AM
Thank you, quite a few things still not entirely clear though:

// ensure matrix manipulations are applied to the matrix reserved for transformations from object space to eye space prior to projection

I'm sorry for such basic questions, but OpenGL books I read, don't go down to a basic level I would need to understand them:
Every object gets multiplied by the modelview matrix each frame, am I correct? Where does this happen in my code example? I know it doesnt in glutSwapBuffers() and glBegin()/glEnd() reference pages only say that they "delimit" the drawn vertices. glVertexf() only says that it "draws" the vertex. Now where is the modelview matrix actually applied to the vertex? The documentation to glTranslatef() says that everything AFTER the translate call will be multiplied by the glMatrixMode() specified matrix. Well how and where?

// Apply a viewing transformation to translate the eye along the Z axis (in this case -50 means the eye will be at +50 since this moves vertices bt -50
glTranslatef(0, 0, -50);

Likewise, a sentence like this baffles me. You say that the vertices will be moved, and at the same time the eye position is translated. I have read that OpenGL does not distinguish between model and view transformations, but could you elaborate on that? What actually happens? It can't be both right, in DirectX the view and model transformations are distinctly different.

At the same time, I wonder if this glTranslate() call was correctly called a world transformation? If glTranslate() always results in a viewing transformation, there is no difference between object space and world space.

Nothing to do with glPush/glPop, see the "depth buffer" technique. You really need to start reading a tutorial about opengl to understand the basics of computer graphics, then this stuff will be clearer to you.
Ahh, I didn't realize that both quads were at the same z position. Shouldn't there be z-fighting? (there isn't. I suppose the precision so close to the camera is so high, that 10.0f and 10.0f can be interpreted as different float numbers internally.)
I'll read up on this matter in a few minutes and edit the post later.
edit1: alright, seems like the default value for the depth test is GL_LESS, which means that it is only logical that the second(red) quad will lie behind the first one, because the z value is equal, not less.

dletozeun
06-06-2009, 04:45 AM
When you define a vertex with the glVertex command, this one is sent to the hardware as it is and is then transformed on the hardware by the matrix you set when you defined it.

So on the client side, the vertex data stays the one you specified and it undergo various transformations on the server side (hardware).

have read that OpenGL does not distinguish between model and view transformations, but could you elaborate on that?

That is true. However you can assume that the model transformation is the one that transforms vertices from object space to world space and the view one, from the world space to eye space.
Actually, before the vertices projection, OpenGL supposes that they are in the eye space, which corresponds the z axis orthogonal to the screen and pointing toward you. x and y axis are supposed to be in the screen plane.

If you set the modelview matrix to identity, vertices are then supposed to be in this eye space.

You say that the vertices will be moved, and at the same time the eye position is translated

Actually only vertices are moved, not the eye. It is sometimes convenient to say that the eye is moving with regard the camera transformations that affect the entire scene.

Shouldn't there be z-fighting?

You will probably get z-fighting depending on the [znear zfar] interval you set. The depth buffer is not that precise as you will realize latter.
To see one of the two quads before the other one, enable depth buffering and add a little offset along z axis to avoid z-fighting.

Karottensuppe
06-06-2009, 06:19 AM
Wonderful explanations, thank you. The last thing about matrix stacks I don't understand, is what people mean when they talk about object and world space especially in relation to glPushMatrix().
Sometimes I run into code where I have to reverse the order of calls inside glPushMatrix()/glPopMatrix() to make it work appropiately. I cannot explain this yet.
How do I move an object only in object space? This should however move it in world space aswell. Don't I always move objects in object space? Essentially I'm asking how to e.g. know if a call to glRotatef() rotates an object, or a coordinate system and which one (object or world coordinate system).
Also there is a somewhat strange explanation floating around the web, that while in object coordinate space, I have to read glPushMatrix()/popMatrix() block in reverse.

dletozeun
06-06-2009, 10:59 AM
I think you do not understand clearly the concepts of object space and world space.

The object space is a space that is proper to the object. Each object has its own object space and its vertices coordinates are expressed in this space. If you are used to some modelers like blender, Maya or 3dsmax each object has its pivot point which is somehow the origin the its object space.

Now the world space is space in which you will place all your objects. To place objects in the scene you just transform them by the world matrix and you will get the position of each one relative the the world origin.

This has nothing to do with glPush/glPop which are commands that are only used to save and restore a matrix state.

I have to read glPushMatrix()/popMatrix() block in reverse.

Not sure to follow you here. If you do something like:

glRotatef(...)
glTranslatef(...)

then all stuff drawn after these calls are affected by the translation and then by the rotation. This is due to the nature of matrix operations.

Dark Photon
06-06-2009, 12:09 PM
The last thing about matrix stacks I don't understand, is what people mean when they talk about object and world space especially in relation to glPushMatrix().
When you do a PushMatrix, it never changes the value at the head of the matrix stack, so conceptually a PushMatrix doesn't do anything to modify the active transform.

How do I move an object only in object space? This should however move it in world space as well. Don't I always move objects in object space?

Recall:

MODELING TRANSFORM * (OBJECT SPACE) = (WORLD SPACE)
VIEWING TRANSFORM * (WORLD SPACE) = (EYE SPACE)
or if we plug the 1st into the 2nd, we get:
VIEWING * MODELING TRANSFORMS * (OBJECT SPACE) = (EYE SPACE)
i.e.
MODELVIEW * (OBJECT SPACE) = (EYE SPACE)

Now let's take your questions. If you fix your MODELING and VIEWING transforms (i.e. the MODELVIEW matrix) and you tweak your OBJECT SPACE positions, then you can see that the WORLD SPACE positions will change too -- basic math, right? So will the eye-space positions. Right? So given a constant MODELVIEW, if we tweak object space positions, world space positions change too.

However, what if we didn't hold MODELVIEW constant, but instead augmented it to counteract the change we just made to the OBJECT SPACE positions? Then our OBJECT SPACE positions could have changed but our WORLD SPACE positions could be the same as before. Make sense? For instance, translate the OBJECT SPACE positions +5 units in X, but then put a -5 units in X translation in MODELVIEW. Net result? World space is the same (to floating point precision of course).

Now to your last question, suppose we held our OBJECT SPACE positions constant, and we only tweaked the MODELVIEW matrix. Then our OBJECT SPACE positions would remain the same, while our WORLD SPACE positions would have changed. Right?

Essentially I'm asking how to e.g. know if a call to glRotatef() rotates an object, or a coordinate system and which one (object or world coordinate system).

That's just a conceptual math trick you can use to understand what's going on. If you read the transforms one direction you're transforming the objects. If you read the transforms in the other direction, you're transforming the coordinate systems. If you want to think about OpenGL transforms in terms of transforming objects, you typically have to read them in the reverse order that you multiply them with the current MODELVIEW matrix. In other words, start closest to your triangle draw calls and read "backwards"

For a description of how applying a single OpenGL transform actually works, see the red book, or read this post (http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=258186#Post258186).

Also there is a somewhat strange explanation floating around the web, that while in object coordinate space, I have to read glPushMatrix()/popMatrix() block in reverse.
I can't make heads or tails of that one.