Discussion:
matrix multiplications and transforms
Diggory Hardy
2007-02-23 20:38:49 UTC
Permalink
Hi,

I've been building my own project for a while, until recently using
OpenGL directly for rendering. I've made my own quaternion class for
this, generating matrices to multiply the opengl modelview and
projection matrices, and it all worked properly.

I recently started implementing OSG into my project to replace the
direct use of OpenGL, and have yet to work out quite how to implement
all the transforms (currently I can see things, but in a very wrong
way). I read in the osg::Matrixd class source file that OSG uses
post-multiplication (why not use premultiplication like everyone else,
including OpenGL?), so I'm assuming the martices should be such that vM
is the transformed version of a vector v. After having worked out
roughly how to use cameras, etc, I have roughly the following structure
(where a -> b means b is a child of a, and a -># b means a has multiple
children like b):

osgUtil::SceneView -> osg::Group -># osg::CameraNode -> osg::Group (root
of the real graph; this is the same object for all cameranodes) ->#
osg::MatrixTransform -> osg::Node (this is an object loaded by osgDB)

The osg::MatrixTransform is set to R*T where R is a rotation matrix and
T a translation matrix (for the position/orientation of the object
attached as a child).

I noticed that for the osg::CameraNode, it's possible to set both the
modelview and the projection matrix. I've left the modelview matrix as
the identity, and set the projection matrix to:
object.invTransMatrix() * camera.invTransMatrix() *
osg::Matrixd.makeFrustum(...)
where invTransMatrix() gives (-T)*(R^-1) [i.e. a negative translation to
move to the origin, then an inverse rotation], for the camera object
position [object], and the camera position relative to the object [camera].

I then render the scene via:
sceneView->setFrameStamp(...)
sceneView->update()
sceneView->cull()
sceneView->draw()

So could anyone point out what I've done wrong?

Some questions I have regarding this:

Will adding the cameranodes to the sceneview like this work properly
or is there a better way? (The cameranodes each have a unique viewport
and all point to the same data)

How are the matrices (for both the transforms and the
cameranodes/sceneview) handled? Are they just multiplied together in
some order (what?) and passed to OpenGL as the modelview/projection
matrices (in which case premultiplication is used for the final vertex
adjustment, right?) ?


In short, if anyone could point out what I've done wrong or answer the
last questions above I would be grateful.

Diggory Hardy
Paul Martz
2007-02-23 21:17:41 UTC
Permalink
Post by Diggory Hardy
(why not use premultiplication like
everyone else, including OpenGL?)
???

1) The OpenGL spec uses postmultiplication notation. If the current top of
matrix stack is C, and you call glMultMatrix( M ), then the new top of
matrix stack is CM. See OpenGL specification under MultMatrix.

2) It's just a notational convention anyway. Again, see the OpenGL spec
under MultMatrix.

Finally, I believe that if you have a MatrixTransform parent containing a
translation, and a MatrixTransform child containing a scale, then the
results would be the same as if you had made the following OpenGL calls:
glTranslatef()
glScalef()

Robert, please correct me if I'm wrong here. I'm just getting to writing the
QSG section on matrices...
-Paul
Robert Osfield
2007-02-23 21:23:55 UTC
Permalink
Post by Paul Martz
Robert, please correct me if I'm wrong here. I'm just getting to writing the
QSG section on matrices...
You seem on track to me.

OpenGL books do use M x v ordering vs. the OSG's v x M.

OSG started off use post multiplication I guess because that's what we
were used to, we did discuss changing a number of years back but there
was non consensus on changing so we stuck with what we have.

I do still wonder about changing it to be consistent with OpenGL books.


Robert.
Jan Ciger
2007-02-23 21:59:57 UTC
Permalink
Post by Robert Osfield
Post by Paul Martz
Robert, please correct me if I'm wrong here. I'm just getting to writing the
QSG section on matrices...
You seem on track to me.
OpenGL books do use M x v ordering vs. the OSG's v x M.
OSG started off use post multiplication I guess because that's what we
were used to, we did discuss changing a number of years back but there
was non consensus on changing so we stuck with what we have.
I do still wonder about changing it to be consistent with OpenGL books.
I think that Diggory meant the column-major vs. row-major convention of the
matrices (OpenGL is column-major, OSG is row-major). This is indeed an easy
trap for the unwary when one starts accessing the members of the osg::Matrix
classes directly - you need to reverse everything and add a transposition
here and there if you are rewriting code to use OSG. This could be a worthy
addition to the documentation on the osg::Matrix class.

Regarding the use of the pre/post-multiplication terms - Paul, please, be very
explicit with what you mean in the book you are preparing. This is frequently
the source of confusion in many books, I have seen somewhere an author say
that "we post-multiply ..." and he multiplies M . v because the vector is
*after* the matrix, instead of referring to the matrix as Robert does. I
prefer to not use these terms at all and to write down the ordering
explicitly instead in order to avoid any confusion when lecturing or speaking
to somebody.

Regards,

Jan
--
Jan Ciger
GPG public key: http://www.keyserver.net/
Paul Martz
2007-02-23 23:06:53 UTC
Permalink
Thanks, I will take care as I document this. I think I understand the
difference, but Jan and Robert please read what I have below and verify I've
got things correct.

Though OpenGL uses column-major notation, an orthonormal basis stored in
that matrix has axis and origin elements stored consecutively. That is, the
new X-axis is in elements [ 0 1 2 ], the new Y axis in elements [ 4 5 6 ],
etc.

The situation is the same in OSG: OSG does not have to transpose its matrix
before calling glLoadMatrix[fd]().

The order of the operands for concatenation and vector-matrix multiplication
in OSG are "backwards" from OpenGL notation.

'row' and 'col' parameters to Matrix::operator() refer to a C-style
row-major 2x2 array. Thus "row 0" in an OSG matrix contains OpenGL matrix
elements [ 0 1 2 3 ] (or OpenGL "column 0").

(I hope this hasn't added to the confusion? Seriously, I was trying to
clarify things with this post... :-)
-Paul
-----Original Message-----
Sent: Friday, February 23, 2007 3:00 PM
To: osg users
Subject: Re: [osg-users] matrix multiplications and transforms
Post by Robert Osfield
Post by Paul Martz
Robert, please correct me if I'm wrong here. I'm just getting to
writing
the
Post by Robert Osfield
Post by Paul Martz
QSG section on matrices...
You seem on track to me.
OpenGL books do use M x v ordering vs. the OSG's v x M.
OSG started off use post multiplication I guess because
that's what we
Post by Robert Osfield
were used to, we did discuss changing a number of years
back but there
Post by Robert Osfield
was non consensus on changing so we stuck with what we have.
I do still wonder about changing it to be consistent with
OpenGL books.
I think that Diggory meant the column-major vs. row-major
convention of the matrices (OpenGL is column-major, OSG is
row-major). This is indeed an easy trap for the unwary when
one starts accessing the members of the osg::Matrix classes
directly - you need to reverse everything and add a
transposition here and there if you are rewriting code to use
OSG. This could be a worthy addition to the documentation on
the osg::Matrix class.
Regarding the use of the pre/post-multiplication terms -
Paul, please, be very explicit with what you mean in the book
you are preparing. This is frequently the source of confusion
in many books, I have seen somewhere an author say that "we
post-multiply ..." and he multiplies M . v because the vector is
*after* the matrix, instead of referring to the matrix as
Robert does. I prefer to not use these terms at all and to
write down the ordering explicitly instead in order to avoid
any confusion when lecturing or speaking to somebody.
Regards,
Jan
--
Jan Ciger
GPG public key: http://www.keyserver.net/
Jan Ciger
2007-02-23 23:28:31 UTC
Permalink
Post by Paul Martz
Thanks, I will take care as I document this. I think I understand the
difference, but Jan and Robert please read what I have below and verify I've
got things correct.
Though OpenGL uses column-major notation, an orthonormal basis stored in
that matrix has axis and origin elements stored consecutively. That is, the
new X-axis is in elements [ 0 1 2 ], the new Y axis in elements [ 4 5 6 ],
etc.
The situation is the same in OSG: OSG does not have to transpose its matrix
before calling glLoadMatrix[fd]().
Hmm, I am not sure whether I am completely following (I didn't program raw
OpenGL in a while), however the typical case where things fail is this:

I want to translate a point [x,y,z] by 10 units in the 'x' direction (i.e. to
the right). In OSG, the matrix and multiplication look like:

[x,y,z,1] * | 1 0 0 0 |
| 0 1 0 0 |
| 0 0 1 0 |
| 10 0 0 1 |

When dealing with OpenGL, the matrix looks like:

| 1 0 0 10 | |x|
| 0 1 0 0 | * |y|
| 0 0 1 0 | |z|
| 1 0 0 1 | |1|

The glLoadMatrix is documented here (including the diagram):
http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/gl/loadmatrix.html

And the OSG implementation is clear from the file
src/osg/Matrix_implementation.cpp in the OSG source.

To me it seems that you need to transpose, unless I am missing something
really obvious. The trick could be in the way the matrices *in memory* are
stored and interpreted, but I am too fried now to think clearly about this.
Post by Paul Martz
(I hope this hasn't added to the confusion? Seriously, I was trying to
clarify things with this post... :-)
-Paul
:)

Jan
--
Jan Ciger
GPG public key: http://www.keyserver.net/
Paul Martz
2007-02-23 23:45:46 UTC
Permalink
I agree with your post, and I think the "apparent" difference between your
comments and mine (even though both are right) is explained by how the
matrices are laid out in memory.

My statement was intended to declare that both OpenGL and OSG matrices are
laid out the same in memory. The "10" in your translation matrix, below, is
the 13th floating point value in memory, and that's true for both OpenGL and
OSG.
-Paul
-----Original Message-----
Sent: Friday, February 23, 2007 4:29 PM
To: osg users
Subject: Re: [osg-users] matrix multiplications and transforms
Post by Paul Martz
Thanks, I will take care as I document this. I think I
understand the
Post by Paul Martz
difference, but Jan and Robert please read what I have below and
verify I've got things correct.
Though OpenGL uses column-major notation, an orthonormal
basis stored
Post by Paul Martz
in that matrix has axis and origin elements stored
consecutively. That
Post by Paul Martz
is, the new X-axis is in elements [ 0 1 2 ], the new Y axis in
elements [ 4 5 6 ], etc.
The situation is the same in OSG: OSG does not have to
transpose its
Post by Paul Martz
matrix before calling glLoadMatrix[fd]().
Hmm, I am not sure whether I am completely following (I
didn't program raw OpenGL in a while), however the typical
I want to translate a point [x,y,z] by 10 units in the 'x'
direction (i.e. to the right). In OSG, the matrix and
[x,y,z,1] * | 1 0 0 0 |
| 0 1 0 0 |
| 0 0 1 0 |
| 10 0 0 1 |
| 1 0 0 10 | |x|
| 0 1 0 0 | * |y|
| 0 0 1 0 | |z|
| 1 0 0 1 | |1|
http://www.opengl.org/documentation/specs/man_pages/hardcopy/G
L/html/gl/loadmatrix.html
And the OSG implementation is clear from the file
src/osg/Matrix_implementation.cpp in the OSG source.
To me it seems that you need to transpose, unless I am
missing something really obvious. The trick could be in the
way the matrices *in memory* are stored and interpreted, but
I am too fried now to think clearly about this.
Post by Paul Martz
(I hope this hasn't added to the confusion? Seriously, I
was trying to
Post by Paul Martz
clarify things with this post... :-)
-Paul
:)
Jan
--
Jan Ciger
GPG public key: http://www.keyserver.net/
Tim Moore
2007-02-24 04:55:36 UTC
Permalink
Post by Paul Martz
I agree with your post, and I think the "apparent" difference between your
comments and mine (even though both are right) is explained by how the
matrices are laid out in memory.
My statement was intended to declare that both OpenGL and OSG matrices are
laid out the same in memory. The "10" in your translation matrix, below, is
the 13th floating point value in memory, and that's true for both OpenGL and
OSG.
-Paul
This is absolutely true. In other words, you can pass an OSG matrix to
glLoadMatrix unchanged and it will do the right thing. Or, said in still
another way, pre-multiplication of a column vector by a matrix is
equivalent to post-multiplicatoin of a row vector by the transpose of
the matrix.

Some of the confusion comes from the "row-major" storage order of C; C
programmers are used to thinking of the first subscript as accessing the
row; others think of it as addressing the column.

IIRC Iris GL described its transforms as post-multiplication of row
vectors; again, even though OpenGL specifies pre-multiplication of
column vectors, the matrices themselves are absolutely compatible
between the two systems.

Tim
Post by Paul Martz
-----Original Message-----
Sent: Friday, February 23, 2007 4:29 PM
To: osg users
Subject: Re: [osg-users] matrix multiplications and transforms
Post by Paul Martz
Thanks, I will take care as I document this. I think I
understand the
Post by Paul Martz
difference, but Jan and Robert please read what I have below and
verify I've got things correct.
Though OpenGL uses column-major notation, an orthonormal
basis stored
Post by Paul Martz
in that matrix has axis and origin elements stored
consecutively. That
Post by Paul Martz
is, the new X-axis is in elements [ 0 1 2 ], the new Y axis in
elements [ 4 5 6 ], etc.
The situation is the same in OSG: OSG does not have to
transpose its
Post by Paul Martz
matrix before calling glLoadMatrix[fd]().
Hmm, I am not sure whether I am completely following (I
didn't program raw OpenGL in a while), however the typical
I want to translate a point [x,y,z] by 10 units in the 'x'
direction (i.e. to the right). In OSG, the matrix and
[x,y,z,1] * | 1 0 0 0 |
| 0 1 0 0 |
| 0 0 1 0 |
| 10 0 0 1 |
| 1 0 0 10 | |x|
| 0 1 0 0 | * |y|
| 0 0 1 0 | |z|
| 1 0 0 1 | |1|
http://www.opengl.org/documentation/specs/man_pages/hardcopy/G
L/html/gl/loadmatrix.html
And the OSG implementation is clear from the file
src/osg/Matrix_implementation.cpp in the OSG source.
To me it seems that you need to transpose, unless I am
missing something really obvious. The trick could be in the
way the matrices *in memory* are stored and interpreted, but
I am too fried now to think clearly about this.
Post by Paul Martz
(I hope this hasn't added to the confusion? Seriously, I
was trying to
Post by Paul Martz
clarify things with this post... :-)
-Paul
:)
Jan
--
Jan Ciger
GPG public key: http://www.keyserver.net/
_______________________________________________
osg-users mailing list
http://openscenegraph.net/mailman/listinfo/osg-users
http://www.openscenegraph.org/
Diggory Hardy
2007-02-24 12:57:25 UTC
Permalink
Thanks for your input. Having transpose matrices is definitely not my
problem (I've checked how the matrices are stored and tried both ways
anyway), but it could be a pitfall for some people.

Reading through your posts I have picked up on something though; I
didn't realise quite what the multiplication order was: I knew vectors v
were pre-multiplied as M(v) = M*v:
[ ] ( )
[ M ] * ( v )
[ ] ( )
(yes it's a 4x4 matrix; I'm just showing the ordering and column vector)
What I then assumed was that matrix adjustments by a matrix T were also
pre-multiplied (when I say pre/post-multiplication, I mean the
adjustment matrix is pre/post): TM --- but they're not, they're
post-multiplied: MT (where M is the original matrix and T is the
adjustment - rotation, translation, whatever). This does actually make
sense now I think of it..

So the total multiplication order, where F is the frustum matrix, M1 the
first matrix affecting v, M2 the second, is like this:
F * M2 * M1 * v
For both OpenGL and OSG. Right? Well maybe not. I tried just
reimplementing it like this, and then creating the frustum matrix as the
transposed version of what Matrix_implementation.cpp creates (so that
the results should be the same as direct OpenGL calls) and both times
ended up with a rather messy screen as a result.

First, could someone please tell me how to REALLY disable automatic
computation of the near and far clipping planes (because at the moment
everything's clipped to a narrow band, strangely, one that isn't
parallel to the screen) ? I've already called:
cam->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR)
on all my osg::CameraNode s and:
setComputeNearFarMode(osgUtil::CullVisitor::DO_NOT_COMPUTE_NEAR_FAR)
on my sceneView.

I'm guessing I'm trying to set the matrices in a different way to what
OSG normally does. This might not work well though.

Diggory
Robert Osfield
2007-02-24 13:40:38 UTC
Permalink
Hi Doggory,

The OSG convention is:

v' = v * M

So transforming a proint for objects space to eye is:

v_eye = v_object * ModelMatrix * ViewMatrix;

Into clip space is:

v_clip = v_object * ModelMatrix * ViewMatrix * ProjectionMatrix;


Into final window coords:

v_clip = v_object * ModelMatrix * ViewMatrix * ProjectionMatrix * WindowMatrix;

Robert.
Post by Diggory Hardy
Thanks for your input. Having transpose matrices is definitely not my
problem (I've checked how the matrices are stored and tried both ways
anyway), but it could be a pitfall for some people.
Reading through your posts I have picked up on something though; I
didn't realise quite what the multiplication order was: I knew vectors v
[ ] ( )
[ M ] * ( v )
[ ] ( )
(yes it's a 4x4 matrix; I'm just showing the ordering and column vector)
What I then assumed was that matrix adjustments by a matrix T were also
pre-multiplied (when I say pre/post-multiplication, I mean the
adjustment matrix is pre/post): TM --- but they're not, they're
post-multiplied: MT (where M is the original matrix and T is the
adjustment - rotation, translation, whatever). This does actually make
sense now I think of it..
So the total multiplication order, where F is the frustum matrix, M1 the
F * M2 * M1 * v
For both OpenGL and OSG. Right? Well maybe not. I tried just
reimplementing it like this, and then creating the frustum matrix as the
transposed version of what Matrix_implementation.cpp creates (so that
the results should be the same as direct OpenGL calls) and both times
ended up with a rather messy screen as a result.
First, could someone please tell me how to REALLY disable automatic
computation of the near and far clipping planes (because at the moment
everything's clipped to a narrow band, strangely, one that isn't
cam->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR)
setComputeNearFarMode(osgUtil::CullVisitor::DO_NOT_COMPUTE_NEAR_FAR)
on my sceneView.
I'm guessing I'm trying to set the matrices in a different way to what
OSG normally does. This might not work well though.
Diggory
_______________________________________________
osg-users mailing list
http://openscenegraph.net/mailman/listinfo/osg-users
http://www.openscenegraph.org/
Paul Martz
2007-02-26 23:06:41 UTC
Permalink
I've written the following so far for the Quick Start Guide, which I believe
summarizes the situation.

One thing I had considered adding was something along the lines of: "If you
already have your own vector/matrix library designed around OpenGL notation,
you do not need to scrap/rewrite it; your matrices will work with OSG." This
is key to anyone porting OpenGL code to OSG, I think.
-Paul



Matrices and osg::Matrix

The osg::Matrix class stores and allows operations on a 4×4 matrix
consisting of 16 floating point numbers. MatrixTransform uses Matrix
internally to store the transformation. You configure MatrixTransform by
configuring its Matrix.

Matrix provides an interface that is somewhat backwards from the notation
used in the OpenGL specification and most OpenGL textbooks. Matrix exposes
an interface consistent with two-dimensional row-major C/C++ arrays:

osg::Matrix m;

m( 0, 1 ) = 0.f; // Set the second element (row 0, column 1)

m( 1, 2 ) = 0.f; // Set the seventh element (row 1, column 2)

OpenGL matrices are one-dimensional arrays, which OpenGL documentation
usually displays as column-major:



GLfloat m;

m[1] = 0.f; // Set the second element

m[6] = 0.f; // Set the seventh element

In spite of this apparent difference, both OSG and OpenGL matrices are laid
out in memory identically—OSG doesn’t perform a costly transpose before
submitting its matrix to OpenGL. As a developer, however, remember to
transpose an OSG matrix in your head before accessing individual elements.

Matrix exposes a comprehensive set of operators for vector-matrix
multiplication and matrix concatenation. To transform a Vec3 v by a rotation
R around a new origin T, use the following code:

osg::Matrix T;

T.makeTranslate( x, y, z );

osg::Matrix R;

R.makeRotate( angle, axis );

Vec3 vPrime = v * R * T;

OpenGL documentation usually illustrates this using postmultiplcation
notation:

v' = TRv

This produces the same result because OpenGL's postmultiplication notation
with column-major matrices is equivalent to OSG's premultiplication
operators with row-major matrices.

To process an entire Geode with this transformation, create a
MatrixTransform node containing T, add a child MatrixTransform node
containing R, and add a child Geode to the rotation MatrixTransform. [TBD
need a figure for this.] This is equivalent to the following sequence of
OpenGL commands:

glMatrixMode( GL_MODELVIEW );

glTranslatef( ... ); // Translation T

glRotatef( ... ); // Rotation R

...

glVertex3f( ... );

glVertex3f( ... );

...

In summary, while OSG exposes a row-major interface that differs from OpenGL
documentation’s column-major notation, OSG and OpenGL perform equivalent
operations internally and their matrices are 100% compatible.

Loading...