Matrix stacks and post-multiplication

hi dudes,

i am fairly new to OpenGL, so this might seem as a stupid question to you.
In one class i am supposed to explain the principals of matrix stacks and matrix post multiplication.

1.) Principals of Matrix stacks.
Well, i know about the glPushMatrix() and glPopMatrix(). These are used to store the transformation matrices, object positions and viewing transformations etc.
all good - i understand this part as how the code works. Unfortunately i dont understand why does OpenGL need these? To save and throw away transformations? or these stacks? Whats the deal? Also, are these stacks saved in the RAM, or the CPU/GPU?

2.) I have to explain Matrix post-multiplication. I’ve tried googling this, but i got more confused. Pre- and post-matrix multiplication just means multiplying a matrix on one side or the other of another vector or matrix - but how is this done in the code? I am a bit confused here.

Help is greatly appreciated!

  1. needed for hierarchical transformation. (i.e the character’s hat is rotated sideways, the character is inside an elevator, the elevator is on the 11th floor, the building is on a hill).
    Yes, matrices are kept in RAM and products of them (i.e the final model-view-projection matrix, the normal matrix, etc) are uploaded to the gpu on demand.
  2. glRotatef() , glTranslatef(), etc do matrix post-multiplication on the current matrix (the modelview, projection or texture matrix).
    Mat4 mat1; mat1.translate(1,2,3); glMultMatrix(&mat1.floats[0]);

Ilian’s taken care of you, but just to add a thing or two that’ll hopefully help it gel.

Regardless of storage order, in math if you have M1 * M2, that has a defined result. Consult any linear algebra book. Rows of M1 multiplied times columns of M2 component-wise, added, and stored in the resulting matrix (row i * column j -> (i,j). Simple. Written like this, M2 is post-multiplied to M1, and M1 is pre-multiplied to M2. Kinda silly even saying it when you write it out like that. :slight_smile: This is all true, regardless of how you store matrices in memory, so row/column order makes no difference here.

Now as to the post-multiplication thing. Why post-multiplication? You may know (from the red book), that OpenGL’s vertex transforms look something like this:

P*V*M * v_obj = v_clip

Where V*M is the MODELVIEW matrix, P is the PROJECTION matrix, v_obj is the object-space vertex position (what you provide to GL), and v_clip is the clip-space vertex position. More simply:

PROJECTION * MODELVIEW * v_obj = v_clip

Let’s suppose you’ve got the MODELVIEW matrix active and then you do a glScale(). What you just did is “post-multiply” the current MODELVIEW matrix by a scale transform, so your entire vertex transform becomes:

P*(V*M*new_scale) * v_obj = v_clip

or more simply:

PROJECTION * ( MODELVIEW_old * new_scale ) * v_obj = v_clip

or:
PROJECTION * MODELVIEW_new * v_obj = v_clip

So that scale is “post-multiplied” onto MODELVIEW because the defined behavior of glScale/glTranslate/and friends is to bolt another transform onto the MODELVIEW transform that’s conceptually (at that point) applied “first” to the object-space vertex position you provide.

It’s definitely a little confusing the first time you trip into this, but hang in there. It gets easier.

What are you seeing that confuses you?

When you do it with pure matrix math on paper, there’s no ambiguity. See my first paragraph above.

It’s only when you put it in code that you have to deal with the silly nuance of row/column major (i.e. matrix element memory storage order). This just refers to how the matrix elements are stored in memory. row-major means rows are stored together. Column-major means columns are stored together.

C/C++ are examples of languages that store data in row-major order. OpenGL is an example of an API that uses a column-major storage-order convention.

Right now you’re probably thinking, oh crap, what do I do about that. But when you sit down and think for a second you realize this is no big deal:

column-major operator-on-the-left == row-major operator-on-the-right

Say what?

Remember OpenGL transform order looks like this: PVM * v1 = v2
This is operator-on-the-left (i.e. matrix on the left of the vector). Also recall OpenGL presumes column-major matrix element storage order. So this is column-major operator-on-the-left.

Turns out, if in C++ you think of the math this way instead: v1 * MVP = v2 (i.e. operator-on-the-right)
and use standard C/C++ row-major element storage order, you get exactly the same data elements in exactly the same memory locations. This is row-major operator-on-the-right. So again:

column-major operator-on-the-left == row-major operator-on-the-right

So to your question as to how you can do OpenGL matrix math in C++ code, you can beat your head against the wall trying to do column-major storage math in row-major C/C++, or you can just flip the thing around to row-major and it’s easy. For instance, instead of post-multiplying transforms in PVMv1=v2 using column-major, you “pre-multiply” transforms in v1MVP=v2 using row-major C++.

Do a little 2x2 matrix * 2x2 matrix example on paper and convince yourself it works. It’ll probably clear up your confusion, or at least refine it into a definite question.

Ilian Dinev thank you for the answers!

and Dark Photon, wow… THANK YOU so much for all the in-depth details! That was really all that i needed, and its fully understood now. Did the math example as well, and it cleared everything up. Thank you for the help, and that you took the time to write, i really appreciate it! Its great to see that there are people who like to share information with others!!

1)In an open source example, the programmer has used the row major matrices and pre multiplication .finally he has passed the results to the glMultMatrix().
Is it possible?
2)The skinning calculation which is used in animation skinning for each vertex v in a bind shape is

where:
• n: The number of joints that influence vertex v
• BSM: Bind-shape matrix
• IBMi: Inverse bind-pose matrix of joint i
• JMi: Transformation matrix of joint i
• JW: Weight of the influence of joint i on vertex v

So it seems that this equation should be used with row major matrices because v( vertex )is on the left side?
So can I use column major matrices and reverse the order of multiplications in this formula? (so i can get the formula : sigma( JW * JMi * IBMi * BSM * v ))