Row major Vs Column Major and RHS vs LHS

Started by
8 comments, last by John Schultz 18 years, 10 months ago
I'm developing a graphics engine and I'm trying to figure out how to keep all my matrices using the same math but since some algorithms use LHS and some use column major I keep getting my math mixed up. I've decided to use a right hand system but now I'm confused on if a rhs determines which ordering the matrices should be? Do they have any effect on if the matrix is row or column major? 11, 12, 13 21, 22, 23, 31, 32, 33 Also for a look at matrix would zAt in row major be 31, 32, 33 and column major would be 13, 23, 33?
Advertisement
This can be confusing. Here's my understanding of it; if I mess up I'm sure someone will correct me.

There are several conventions you have to take into account:

1. Left- vs. right-handed coordinate system
2. Left- vs. right-handed rotation
3. Row vs. column vectors
4. Row- vs. column-major

(Note that by coordinate system handedness I mean the 'hold up your left or right hand' definition, not whether the determinant is + or - 1.)

I don't know if people ever mix coordinate systems and rotations of opposite handedness; we'll just assume that they match. So that reduces to three choices:

1. Left- or right-handed
2. Row or column vectors
3. Row or column major

The important thing to understand is that these choices are all independent of one another; you can mix and match them in any way.

The two leading APIs have adopted specific conventions:

OpenGL:
right-handed
column-vectors
column-major

D3D:
left-handed
row-vectors
row-major

In your own engine you could use whatever combination you wanted, e.g. right-handed, row vectors, column-major.

Also note that the row/column-major issue is only relevant if you are dealing with matrices stored as 1-dimensional arrays. To further confuse the issue, row vectors/row-major and column vectors/column-major actually result in the same memory layout, so OpenGL and D3D matrices are arranged in memory the same way.

Well, this is actually a pretty big topic, and I have to run at the moment, but please ask if you need further clarification.
Thanks, what is the difference between row major and row vector?
Quote:Thanks, what is the difference between row major and row vector?
Quick answer...

Row vs. column vectors

Multiplying a vector and a matrix can be viewed as a matrix-matrix multiplication, where the vector is either a 1x3 matrix (a 'row vector') or a 3x1 matrix (a 'column vector'). Recall that for a matrix multiplication to be valid, the inner dimensions of the matrices must match. Therefore, these are valid:

1x3 * 3x3
3x3 * 3x1

And these are not:

3x3 * 1x3
3x1 * 3x3

From our correct examples above, we see that with row vectors, the vector goes to the left of the matrix, like this:

v' = v*M

And with column vectors it goes to the right:

v' = M*v

There's more, but I said this'd be short :)

Row- vs. column-major

This has nothing to do with algebra; it's purely a programming issue. For various reasons 2-dimensional matrices are often stored as 1-dimensional arrays in memory. OpenGL does this, and as far as I know D3D does also. This raises the question of how the matrix:

m11 m12 m13
m21 m22 m23
m31 m32 m33

Is to be laid out in memory. Like this?

0 1 2
3 4 5
6 7 8

Or like this?

0 3 6
1 4 7
2 5 8

The former is referred to as row-major, because the entries in the 1d array move across the rows first and down the columns second. The latter is the opposite and is referred to as column-major.

By the time you take into account row and column vectors, row- and column-majorness, and coordinate system and rotation handedness, there are plenty of opportunities to screw up. Worse yet, quite often two such errors will cancel each other out and produce the correct results, allowing errors to go unnoticed. So, it is a little complicated until you get a handle on it.
Just to expand on what people have said, bear in mind that while Direct3D and OpenGL use row major and column major matrices respectively, their actual memory configuration is exactly the same.
If at first you don't succeed, redefine success.
Ok - So I've decided to use a Right handed system and Row Major/Vector's for my engine, because I believe most of my code so far has been using that(gotta go back and check it all). Can you help me through some examples to make sure I'm keeping it all straight? Tell me if I got anything wrong.

Here is my matrix decleration
union{struct {	float	_11, _12, _13, _14,		_21, _22, _23, _24,		_31, _32, _33, _34,		_41, _42, _43, _44;};float m_indices[4][4];};


Translation
A translation vector in row major would be

mat.m_indices[3][0] = x;
mat.m_indices[3][1] = y;
mat.m_indices[3][2] = z;

or

mat._41 = x;
mat._42 = y;
mat._43 = z;


Rotation
A rotation matrix in row major would be
mat.m_indices[0][0] = cosf( z ); mat.m_indices[0][1] = -sinf(z);
mat.m_indices[1][0] = sinf( z ); mat.m_indices[2][1] = cosf(z);

or

mat._11 = cosf(z); mat._12 = -sinf(z);
mat._21 = sinf(z); mat._22 = cosf(z);

Perspective Projection Matrix
//xScale 0 0 0
// 0 yScale 0 0
// 0 0 zf/(zn-zf) -1
// 0 0 zn*zf/(zn-zf) 0
m._11 = xScale;
m._22 = yScale;
m._33 = zFar / ( zNear - zFar );
m._34 = -1.0f;
m._43 = zNear * zFar / ( zNear - zFar );

Hopefully, I got everything right!
The translation looks correct; didn't look at the perspective matrix. It's hard to say about the rotation matrix because there are other factors to take into consideration. I think your matrix will produce a rotation of opposite handedness than your coordinate system (which may be what you want). I think the 'standard' matrix would be the transpose of what you have, although I could be wrong.

Also, I don't know much about these sorts of issues but I've heard that using unions like that is bad for portability...
Hmm, I still don't get it

You say that DirectX is row major then why do they store the zAxis, yAxis, and xAxis in column form in a look at matrix, the documentation for D3DXMatrixLookAtRH is this matrix.

zaxis = normal(Eye - At)xaxis = normal(cross(Up, zaxis))yaxis = cross(zaxis, xaxis) xaxis.x           yaxis.x           zaxis.x          0 xaxis.y           yaxis.y           zaxis.y          0 xaxis.z           yaxis.z           zaxis.z          0-dot(xaxis, eye)  -dot(yaxis, eye)  -dot(zaxis, eye)  1


Each axis is stored along the column...I'm confused on how I should know which way to do everything.
Quote:You say that DirectX is row major then why do they store the zAxis, yAxis, and xAxis in column form in a look at matrix, the documentation for D3DXMatrixLookAtRH is this matrix.
First, make sure you're not confusing 'row major' with 'row vectors' (it's easy to do - I've made the same mistake myself). 'Row major' refers to how the matrix is stored in memory and is a completely separate issue from whether you're using row or column vectors.

To answer your question, the basis vectors are in the columns because this is a view transform rather than an object transform, that is, it's the inverse of the camera object matrix. What that means exactly is a fairly large topic and requires some understanding of coordinate system transforms and various matrix operations.
Quote:Original post by ph33r
Hmm, I still don't get it

You say that DirectX is row major then why do they store the zAxis, yAxis, and xAxis in column form in a look at matrix, the documentation for D3DXMatrixLookAtRH is this matrix.

*** Source Snippet Removed ***

Each axis is stored along the column...I'm confused on how I should know which way to do everything.


For a right-handed coordinate system, a positive rotation about an axis is counter-clockwise.

A 2D rotation about Z, postmultiplying by column vector:

x' = cos(a)*x - sin(a)*y
y' = sin(a)*x plus cos(a)*y

Where cos/sin are arranged as they would appear in matrix notation. If the matrix were defined in memory as two vectors, it's clear that the result is from two dot products (working on the rows).

If x,y where 0,1, and a was 45 degrees, x',y' => -.707,.707 (a counter-clockwise rotation). If we wanted to draw the matrix to visualize what it is doing, we would plot the columns as x and y vectors. The y column is -.707,.707 (the same as rotating 0,1 by 45 degrees).

If one were to premultiply by a row vector, the matrix would need to be transposed. If the D3DXMatrixLookAtRH() is creating a view matrix (transpose of an object to world matrix), then it appears they are using premultiplied row vectors.

There are only two possiblities to fix a matrix error related to the above questions: matrix transpose and/or flip z direction. Decide on a convention and make sure all of your code matches.

See also:
http://www.evl.uic.edu/ralph/508S98/coordinates.html
http://stevehollasch.com/cgindex/math/matrix/column-vec.html (excellent history)

This topic is closed to new replies.

Advertisement