20

Let's say I have a blue 3D box (where the top side is red colored).

  1. Now I call glScalef(1, 10, 1).
  2. Then I call glRotatef(90, 0, 1, 0).
  3. Then i render the cube.

What i expected is to see the the red side facing and stretched towards the screen (along the Y axis of the model).

But what im seeing is this: The red side is facing the screen (as expected). But the stretch is occurring on the Y axis of the view space (not the model).

I do know that if I set the scale along the Z axis, then i'll get the right result. But my confusion is that i thought scaling up on the Y axis, then rotating the box, would give me the right result.

What am i missing?

genpfault
  • 51,148
  • 11
  • 85
  • 139
AlvinfromDiaspar
  • 6,611
  • 13
  • 75
  • 140

2 Answers2

27

OpenGL reads its matrices in what is known as column major order. The result is that each subsequent operation is a pre-multiplication of all the operations before it, not a post-multiplication. Thus, each subsequent call to an OpenGL operation (glScale, glTranslate, glRotate) is the first thing that affects an object, and it goes in reverse order from how you write it.

So, you need to change your multiplication order:

glRotatef(90, 0, 1, 0); // Notice how this is the first operation ...
// ... but will be applied after
glScalef(1, 10, 1); // This will be applied first, but is the last operation
// Zany, right?

Truth be told, it feels less intuitive when you're writing programs and using a computer to have to specify things in reverse order. But the Graphics Gods That Be decided that's how they wanted OpenGL's matrix stack to behave (along with HLSL, GLSL, and other systems: see the link above about column-major ordering).

  • 4
    can you explain what storing data in column-major order has to do with the order of operations? –  Jun 08 '14 at 14:33
  • 1
    See http://stackoverflow.com/questions/17717600/confusion-between-c-and-opengl-matrix-order-row-major-vs-column-major for a better discussion. – ToolmakerSteve Nov 06 '14 at 08:10
  • 2
    Some background on this (or the Graphics Gods' reasoning): http://steve.hollasch.net/cgindex/math/matrix/column-vec.html – Vitaly Jun 25 '15 at 14:08
  • @Vitaly very interesting link. – user10607 Feb 28 '16 at 06:32
0

If you have an array which represents a 4x4 matrix like this:

float mat[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };

and you load this into an OpenGL matrix, OpenGL will see the first 4 floats as the first column of the matrix, the second 4 floats as the second column and the third 4 floats as the 3rd column of the matrix, etc, etc.

So, every 4 floats, OpenGL sees as another column. This is known as Column-Major order. It stores the data in columns. If you had to transpose a column-major matrix, it'd end up as a row-major matrix, and vice-versa.

Because you've got a column-major matrix, you also need to use column vectors, which means your order of multiplication will be: M*v.

To prove this to yourself, take a simple 2x2 matrix with a 2x1 column vector, multiply as M*v. Let K=transpose(M), and r=row vector (1x2).

Then M * v = r * K

Bottom line, once you use a column or row major notation in your code, you have to stick to it.

Michael
  • 84
  • 6
  • 3
    The storage order in memory (row-major vs. column-major) is unrelated to the choice of multiplying vectors as columns on the right or as rows on the left. For example, you can multiply vectors from the left in GLSL, even though the matrices are stored in column-major order. – Reto Koradi Dec 17 '14 at 05:47
  • Yes you can multiply from the left in GLSL, but you have to remember to transpose the matrix. That's why it's just better to stick to one set method -- less confusing -- just answering racarate's question. Thanks. – Michael Dec 18 '14 at 09:48