# Why does this matrix multiplication order matter?

## Recommended Posts

This is how I used to do matrix multiplication in my vertex shaders:


gl_Position = projection * (view * (model * position));


But since I've ported to linux, in at least two of my shaders, only this works:


gl_Position = projection * view * model * position;


Why?

##### Share on other sites

when you set your uniform matricies in linux, the glsl perhaps assumes opposite majoring order. So in case of column you use row, or vice versa (since you just ported, thus the layout in memory is the same). If you transpose the matricies, it will work with the previous formula (I am not saying you should do that of course, a waste of instructions).

##### Share on other sites

No, this works with all my other shaders.

Also, seems to be a bug:


float minlight = shadow * diffuse;

//outfrag = vec4(color.xyz * stexel.xyz * shadow * diffuse + vspecular, alph);	//buggy on linux for some reason
outfrag = vec4(color.xyz * stexel.xyz * minlight + vspecular, alph);


It produces a reddish colour with blue fragments.

GL_VERSION: 3.0 Mesa 10.3.0-devel (git-96a95f4 saucy-oibaf-ppa)
Renderer1: Mesa DRI Intel(R) Ivybridge Mobile

Maybe it's because I tried to get the latest (experimental?) drivers for my integrated Intel videocard?

Edited by polyfrag

##### Share on other sites

GLSL code is platform agnostic for the most part, them difference usually boils down to the shader compiler included with the OpenGL driver. So this may be a driver bug.

##### Share on other sites

No, this works with all my other shaders.

What no? Do you mean shaders on linux that use the first formula work, and only this shader demands second formula on the linux?

Again, GLSL does not make distinction betweeen column or row vectors in its very code, but order of operations is explicit of course (order is actualy equivalent of majoring). you would better use glUniformMatrix4fv with transpose parameter specified, this will interpet matrix in GLSL code as expected, if driver is not totaly mad.

color.xyz * stexel.xyz

this is a cross product, depends on majoring as well.

I myself am not constructing matricies in GLSL code by implicit constructors, but I use explicit more atomic operations to actualy transform vectors by them, safely. (since I do not use glUniformMatrix4fv call to set matricies, but a batched vec4 array where I put everything I need for the shader in one call).

All operations that result in a vector not scalar I perform myself explicitly atomic for they are sensitive for majoring, to make myself sure.(this includes transformation of vector, multiplying matricies, crossing vectors...).

##### Share on other sites

color.xyz * stexel.xyz

this is a cross product, depends on majoring as well.

No, that's a pairwise multiply, not a cross product. The cross product is done with cross() and it does not depend on the row/column majorness of matrices.

polyfrag's code doesn't have matrix memory layout problems. His problem is that associative property of matrix multiplication doesn't hold on his Linux machine. And I don't know the reason for that.

##### Share on other sites

Again, GLSL does not make distinction betweeen column or row vectors in its very code, but order of operations is explicit of course (order is actualy equivalent of majoring).

The difference in the order is whether to multiply the vector first, and have all the other matrixes multiply a vector (reducing the number of operations since a vector is only 4x1), or multiply all the matrixes in order and only multiply the vector at the end.

Edited by polyfrag

##### Share on other sites

No, that's a pairwise multiply, not a cross product. The cross product is done with cross() and it does not depend on the row/column majorness of matrices.

polyfrag's code doesn't have matrix memory layout problems. His problem is that associative property of matrix multiplication doesn't hold on his Linux machine. And I don't know the reason for that.

Ah, yes you are right,

But memory layout is not what I am apointing. Majoring of operations has nothing to do with layout in memory. To explain, if you transform a matrix and reverse the majoring (component wisdom), it is then the same matrix and the same oprations- you just have to perform reverse majoring all the time, and, if you return to preveious majoring and keep the new matrix , it is  then a different (transposed matrix). Majoring is equivalent of operations comutativity with vectors and matricies.

If memory layout transposes,  you simply have only two options to have the same matrix, reverse the majoring (order of not comutaive operations with vectors), or, have matrix components to not tranpose thanks to layout missinterpratation.

##### Share on other sites
The difference in the order is whether to multiply the vector first, and have all the other matrixes multiply a vector (reducing the number of operations since a vector is only 4x1), or multiply all the matrixes in order and only multiply the vector at the end.

yes, first formula is more effective since you transform vector 3 times by a matrix, while second formula performs 2 matrix multiplications and than transforms a vector. I stress again that second formula performs reverse order of transformations, and it results in the same thing since matricies are (diagnosticly) transposed.

##### Share on other sites

Again, GLSL does not make distinction betweeen column or row vectors in its very code, but order of operations is explicit of course (order is actualy equivalent of majoring).

The difference in the order is whether to multiply the vector first, and have all the other matrixes multiply a vector (reducing the number of operations since a vector is only 4x1), or multiply all the matrixes in order and only multiply the vector at the end.

That only saves operations if you are doing only one or two transformations.  If you are processing lots of vectors, then the matrix concatenation is clearly more efficient.

Regarding your original question, are you having issues with only one or two shaders, but all of the others are working as you expected (using the same matrix multiplication order)?

##### Share on other sites
You mean if I concatenate the projection, view, and model matrix on the CPU side once before sending it to the shader?

All my other (4 or so) shaders are working with the first method without the bug while the other 3 must be written without brackets as said.

##### Share on other sites

You mean if I concatenate the projection, view, and model matrix on the CPU side once before sending it to the shader?

It shouldn't make a difference (in terms of outcome) though it would probably be faster since you'd only be doing one matrix multiplication per shader invocation instead of three. But it might work around the bug.

All my other (4 or so) shaders are working with the first method without the bug while the other 3 must be written without brackets as said.

Are they written exactly the same? How are the matrix variables (projection, model, etc..) defined?

I've noted strange bugs in GLSL compilers before (including ambiguous and sometimes plain contradictory behaviour between two or more compilers) but, well, matrix multiplication is supposed to be associative. So either there's something in how the matrices are defined that is wrong but used to work by accident before, or (more likely) it's a compiler bug...

yes, first formula is more effective since you transform vector 3 times by a matrix, while second formula performs 2 matrix multiplications and than transforms a vector. I stress again that second formula performs reverse order of transformations, and it results in the same thing since matricies are (diagnosticly) transposed.

Matrix multiplication is associative. For any three matrices A, B, C, of any dimension (where multiplication is defined), A * (B * C) = (A * B) * C. There should be no difference in the result. The order of transformations is the same, this is not about commutativity.

Edited by Bacterius

##### Share on other sites

Matrix multiplication is associative. For any three matrices A, B, C, of any dimension (where multiplication is defined), A * (B * C) = (A * B) * C. There should be no difference in the result. The order of transformations is the same, this is not about commutativity.

He is not performing matrix multiplication but a vector transformation in first formula.

gl_Position = projection * (view * (model * position));

Here asociativity maters, he transforms position vector by the model, then view, then projection matrix

In second formula he performs implicit operator order of multiplying three matrcies (comutativity matters) and transforming a colum vector by them, while the matricies must be transposed since they are in reverse order and transform a column vector resulting in same result as the first formula that transforms a column vector

if the matricies were correct, not transposed, than this

gl_Position = position * (projection * view * model);

is the same result as in the first formula.

As I said, GLSL does not demand a certain layout of a matrix, you decide yourself by binary operator * left/right position where your column is (but for that you need to know where your column is in initiated components)

As I said GLSL majoring is explicit by the operator * between vector and matrix, not by components m,n agreement, the layout is irelevant and can be any. Layout of components is not what algebra demands- you can transform a vector by rows of layout (row matrix v*M) or by columns of layout (column matrix M*v). The unwilingness of people to connect matrix components with atual operation is intense, I am not neat picking.

##### Share on other sites

Matrix multiplication is associative. For any three matrices A, B, C, of any dimension (where multiplication is defined), A * (B * C) = (A * B) * C. There should be no difference in the result. The order of transformations is the same, this is not about commutativity.

He is not performing matrix multiplication but a vector transformation in first formula.

The vector transformation IS a matrix multiplication. As Bacterius said, associativity holds for all sizes, that includes matrices with only one column (typically referred to as column vectors).

##### Share on other sites

It is in reverse order.

I know that transforming a vector is a matrix multiplication. but vector is also a 1 dimensional matrix so I prefer to call it a vector becouse it yelds certain characteristics than non-1dimensional vectors(matricies) do not have.

gl_Position = projection * (view * (model * position));

does not equal this

gl_Position = projection * view * model * position;

To see further, I checked wiki and it says

If four matrices A, B, C, and D are respectively m × p, p × q, q × r, and r × s matrices,

What does not hold for the first  formula. (this is simply the result of vector termin being an inclusion of matrix termin, what makes people think that what is a matrix must be associative just out of the box)

It accidentaly confirms the formula in my previous post, concretely

gl_Position = position * (projection * view * model);

is the same result as in the first formula.

so if OP used this formula (equal to his first one), he could change brackets how ever he thinks and it would provide the same result all the time.

##### Share on other sites

gl_Position = projection * (view * (model * position));

does not equal this

gl_Position = projection * view * model * position;

Yes it does, or, to be more to the point:
projection * (view * (model * position)) = ((projection * view) * model) * position

To see further, I checked wiki and it says

If four matrices A, B, C, and D are respectively m × p, p × q, q × r, and r × s matrices,

What does not hold for the first  formula. (this is simply the result of vector termin being an inclusion of matrix termin, what makes people think that what is a matrix must be associative just out of the box)

Lets see:
A == projection
B == view
C == model
D == position

A is m x p which is 4 x 4 (4 rows, 4 columns)
B is p x q which is 4 x 4 (4 rows, 4 columns)
C is q x r which is 4 x 4 (4 rows, 4 columns)
D is r x s which is 4 x 1 (4 rows, 1 column)

The quote (on wikipedia) goes on to say, that the end result is an m x s matrix, which would be 4 x 1 (4 rows, 1 column).

Seems to me, everything is in order. How does that not hold for the above formula?

It accidentaly confirms the formula in my previous post, concretely

gl_Position = position * (projection * view * model);

is the same result as in the first formula.

so if OP used this formula (equal to his first one), he could change brackets how ever he thinks and it would provide the same result all the time.

I have no idea how you got there, maybe you are confusing it with this identity?

transpose(A * B) = transpose(B) * transpose(A)

With this you can turn

(projection * view * model) * position

into

transpose(transpose(position) * transpose(projection * view * model))

and since GLSL ignores transposes on vectors (IIRC)

gl_Position = position * transpose(projection * view * model)

You can break this further down to

gl_Position = position * transpose(model) * transpose(view) * transpose(projection)

at which point you arrive at the notation preferred in DirectX. So even if you do mess up and provide OpenGL with transposed matrices, you have to reverse the entire product, not just shift position to the front.

##### Share on other sites

I think order does matter. That's what I was taught and my c++ code only works when I multiply (post-multiply?) the projection by the modelview. But I'm told that matrix multiplication is actually done the other way around so what I'm doing is multiplying the modelview by the projection.  Or does it? Something else is wrong with my code.  Yes, I have to post-multiply the projection by the view by the model matrix in that order.

Edited by polyfrag

##### Share on other sites

Yes it does, or, to be more to the point:
projection * (view * (model * position)) = ((projection * view) * model) * position

Nonsense.

D is r x s which is 4 x 1 (4 rows, 1 column)

you just picked it to be row vector,

I have no idea how you got there, maybe you are confusing it with this identity?

It rather seems you are confused.

##### Share on other sites

I think order does matter. That's what I was taught and my c++ code only works when I multiply (post-multiply?) the projection by the modelview. But I'm told that matrix multiplication is actually done the other way around so what I'm doing is multiplying the modelview by the projection.  Or does it? Something else is wrong with my code.  Yes, I have to post-multiply the projection by the view by the model matrix in that order.

Order does matter. The order in which you multiply matrices depends on how you are storing the transformation in them. If you store the basis vectors in the columns of a matrix, then to transform a point you'll do M*p. If you store the basis vector in the rows of a matrix then you'll have to do p*M.

Either way, though, projection * view * model * position should be equal to projection * (view * (model * position)), because (as has been stated many times in this thread by everyone except JohnnyCode) matrix multiplication is associative.

JohnnyCode: since you continue to refuse to listen to anyone about this, why don't you just do the math out by hand?

Here's a sample done for a 2x2 matrix demonstrating that A*B*v == A*(B*v)

// ABv
[a b  * [e f  * [x  = [ae+bg af+bh  * [x  = [x(ae+bg)+y(af+bh)
c d]    g h]    y]    ce+dg cf+dh]    y]    x(ce+dg)+y(cf+dh)]

// A(Bv)
[a b  * ([e f  * [x ) = [a b  * [xe+yf = [a*(xe+yf)+b*(xg+yh)  = [x(ae+bh)+y(af+bh)
c d]     g h]    y]     c d]    xg+yh]   c*(xe+yf)+d*(xg*yh)]    x(ce+dg)+y(cf+dh)]

// they are the same! the same!!


##### Share on other sites

polyfrag, on 22 Jun 2014 - 6:35 PM, said:

All my other (4 or so) shaders are working with the first method without the bug while the other 3 must be written without brackets as said.

Are they written exactly the same? How are the matrix variables (projection, model, etc..) defined?

//projection
Matrix PerspProj(float fov, float aspect, float znear, float zfar)

{

float m[16];

float xymax = znear * tan(fov * PI_OVER_360);

float ymin = -xymax;

float xmin = -xymax;

float width = xymax - xmin;

float height = xymax - ymin;

float depth = zfar - znear;

float q = -(zfar + znear) / depth;

float qn = -2 * (zfar * znear) / depth;

float w = 2 * znear / width;

w = w / aspect;

float h = 2 * znear / height;

m[0]  = w;

m[1]  = 0;

m[2]  = 0;

m[3]  = 0;

m[4]  = 0;

m[5]  = h;

m[6]  = 0;

m[7]  = 0;

m[8]  = 0;

m[9]  = 0;

m[10] = q;

m[11] = -1;

m[12] = 0;

m[13] = 0;

m[14] = qn;

m[15] = 0;

Matrix mat;

mat.set(m);

return mat;

}

//view
Matrix LookAt(float eyex, float eyey, float eyez,

float centerx, float centery, float centerz,

float upx, float upy, float upz)

{

float m[16];

Vec3f x, y, z;

/* Make rotation matrix */

/* Z std::vector */

z = Vec3f(eyex, eyey, eyez) - Vec3f(centerx, centery, centerz);

z = Normalize(z);

/* Y std::vector */

y = Vec3f(upx, upy, upz);

//Vec3f vCross = Cross(m_view - m_pos, m_up);

/* X std::vector = Y cross Z */

x = Normalize(Cross(y, z));

// return Normalize( Cross( m_strafe, m_view - m_pos ) );

/* Recompute Y = Z cross X */

y = Normalize(Cross(z, x));

/* mpichler, 19950515 */

/* cross product gives area of parallelogram, which is < 1.0 for

* non-perpendicular unit-length vectors; so normalize x, y here

*/

#define M(row,col)  m[col*4+row]

//#define M(row,col)  m[row*4+col]

M(0, 0) = x.x;

M(0, 1) = x.y;

M(0, 2) = x.z;

M(0, 3) = 0.0;

//M(0, 3) = eyex;

M(1, 0) = y.x;

M(1, 1) = y.y;

M(1, 2) = y.z;

M(1, 3) = 0.0;

//M(1, 3) = eyey;

M(2, 0) = z.x;

M(2, 1) = z.y;

M(2, 2) = z.z;

M(2, 3) = 0.0;

M(3, 0) = 0.0;

M(3, 1) = 0.0;

M(3, 2) = 0.0;

//M(3, 2) = eyez;

M(3, 3) = 1.0;

//M(3, 0) = -eyex;

//M(3, 1) = -eyey;

//M(3, 2) = -eyez;

//M(3, 3) = 1.0;

#undef M

//glMultMatrixf(m);

Matrix mat;

mat.set(m);

/* Translate Eye to Origin */

//glTranslatef(-eyex, -eyey, -eyez);

Matrix mat2;

float trans[] = {-eyex, -eyey, -eyez};

//float trans[] = {eyex, eyey, eyez};

mat2.translation(trans);

//mat2.inverseTranslateVect(trans);

//Matrix mat;

//float trans[] = {-eyex, -eyey, -eyez};

//mat.translation(trans);

//Matrix mat2;

//mat2.set(m);

/*

g_log<<"------------------------"<<endl;

g_log<<"LookAt "<<endl;

g_log<<"eye="<<eyex<<","<<eyey<<","<<eyez<<" center="<<centerx<<","<<centery<<","<<centerz<<" up="<<upx<<","<<upy<<","<<upz<<endl;

for(int i=0; i<16; i++)

g_log<<"before translation["<<i<<"] = "<<m[i]<<endl;

for(int i=0; i<16; i++)

g_log<<"the translation["<<i<<"] = "<<mat2.m_matrix[i]<<endl;*/

mat.postmult(mat2);

/*

for(int i=0; i<16; i++)

g_log<<"final view matrix, after translation["<<i<<"] = "<<mat.m_matrix[i]<<endl;

g_log<<"------------------------"<<endl;

g_log.flush();

*/

return mat;

}

void Matrix::postmult( const Matrix& matrix )

{

float newMatrix[16];

#if 0

const float *m1 = m_matrix, *m2 = matrix.m_matrix;

newMatrix[0] = m1[0]*m2[0] + m1[4]*m2[1] + m1[8]*m2[2];

newMatrix[1] = m1[1]*m2[0] + m1[5]*m2[1] + m1[9]*m2[2];

newMatrix[2] = m1[2]*m2[0] + m1[6]*m2[1] + m1[10]*m2[2];

newMatrix[3] = 0;

newMatrix[4] = m1[0]*m2[4] + m1[4]*m2[5] + m1[8]*m2[6];

newMatrix[5] = m1[1]*m2[4] + m1[5]*m2[5] + m1[9]*m2[6];

newMatrix[6] = m1[2]*m2[4] + m1[6]*m2[5] + m1[10]*m2[6];

newMatrix[7] = 0;

newMatrix[8] = m1[0]*m2[8] + m1[4]*m2[9] + m1[8]*m2[10];

newMatrix[9] = m1[1]*m2[8] + m1[5]*m2[9] + m1[9]*m2[10];

newMatrix[10] = m1[2]*m2[8] + m1[6]*m2[9] + m1[10]*m2[10];

newMatrix[11] = 0;

newMatrix[12] = m1[0]*m2[12] + m1[4]*m2[13] + m1[8]*m2[14] + m1[12];

newMatrix[13] = m1[1]*m2[12] + m1[5]*m2[13] + m1[9]*m2[14] + m1[13];

newMatrix[14] = m1[2]*m2[12] + m1[6]*m2[13] + m1[10]*m2[14] + m1[14];

newMatrix[15] = 1;

#else

const float *a = m_matrix, *b = matrix.m_matrix;

newMatrix[0]  = a[0] * b[0]  + a[4] * b[1]  + a[8] * b[2]   + a[12] * b[3];

newMatrix[1]  = a[1] * b[0]  + a[5] * b[1]  + a[9] * b[2]   + a[13] * b[3];

newMatrix[2]  = a[2] * b[0]  + a[6] * b[1]  + a[10] * b[2]  + a[14] * b[3];

newMatrix[3]  = a[3] * b[0]  + a[7] * b[1]  + a[11] * b[2]  + a[15] * b[3];

newMatrix[4]  = a[0] * b[4]  + a[4] * b[5]  + a[8] * b[6]   + a[12] * b[7];

newMatrix[5]  = a[1] * b[4]  + a[5] * b[5]  + a[9] * b[6]   + a[13] * b[7];

newMatrix[6]  = a[2] * b[4]  + a[6] * b[5]  + a[10] * b[6]  + a[14] * b[7];

newMatrix[7]  = a[3] * b[4]  + a[7] * b[5]  + a[11] * b[6]  + a[15] * b[7];

newMatrix[8]  = a[0] * b[8]  + a[4] * b[9]  + a[8] * b[10]  + a[12] * b[11];

newMatrix[9]  = a[1] * b[8]  + a[5] * b[9]  + a[9] * b[10]  + a[13] * b[11];

newMatrix[10] = a[2] * b[8]  + a[6] * b[9]  + a[10] * b[10] + a[14] * b[11];

newMatrix[11] = a[3] * b[8]  + a[7] * b[9]  + a[11] * b[10] + a[15] * b[11];

newMatrix[12] = a[0] * b[12] + a[4] * b[13] + a[8] * b[14]  + a[12] * b[15];

newMatrix[13] = a[1] * b[12] + a[5] * b[13] + a[9] * b[14]  + a[13] * b[15];

newMatrix[14] = a[2] * b[12] + a[6] * b[13] + a[10] * b[14] + a[14] * b[15];

newMatrix[15] = a[3] * b[12] + a[7] * b[13] + a[11] * b[14] + a[15] * b[15];

#endif

set( newMatrix );

}

void Matrix::translation( const float *translation )

{

#if 0

m_matrix[0] = m_matrix[5] =  m_matrix[10] = m_matrix[15] = 1.0;

m_matrix[1] = m_matrix[2] = m_matrix[3] = m_matrix[4] = 0.0;

m_matrix[6] = m_matrix[7] = m_matrix[8] = m_matrix[9] = 0.0;

m_matrix[11] = 0.0;

#elif 1

m_matrix[12] = translation[0];

m_matrix[13] = translation[1];

m_matrix[14] = translation[2];

#elif 0

#define M(row,col)  m_matrix[col*4+row]

//#define M(row,col)  m_matrix[row*4+col]

/*

// http://stackoverflow.com/questions/13293469/why-does-my-translation-matrix-needs-to-be-transposed

1, 0, 0, 0

0, 1, 0, 0

0, 0, 1, 0

x, y, z, 1

*/

M(0,0) = 1;

M(0,1) = 0;

M(0,2) = 0;

M(0,3) = translation[0];

M(1,0) = 0;

M(1,1) = 1;

M(1,2) = 0;

M(1,3) = translation[1];

M(2,0) = 0;

M(2,1) = 0;

M(2,2) = 1;

M(2,3) = translation[2];

M(3,0) = 0;

M(3,1) = 0;

M(3,2) = 0;

M(3,3) = 1;

#undef M

#endif

}

//model

float pitch = 0;

Matrix modelmat;

modelmat.translation((const float*)&pos);

Matrix rotation;

modelmat.postmult(rotation);

glUniformMatrix4fv(s->m_slot[SSLOT_MODELMAT], 1, 0, modelmat.m_matrix);

void Matrix::rotrad( const float *angles )

{

double cr = cos( angles[0] );

double sr = sin( angles[0] );

double cp = cos( angles[1] );

double sp = sin( angles[1] );

double cy = cos( angles[2] );

double sy = sin( angles[2] );

m_matrix[0] = ( float )( cp*cy );

m_matrix[1] = ( float )( cp*sy );

m_matrix[2] = ( float )( -sp );

double srsp = sr*sp;

double crsp = cr*sp;

m_matrix[4] = ( float )( srsp*cy-cr*sy );

m_matrix[5] = ( float )( srsp*sy+cr*cy );

m_matrix[6] = ( float )( sr*cp );

m_matrix[8] = ( float )( crsp*cy+sr*sy );

m_matrix[9] = ( float )( crsp*sy-sr*cy );

m_matrix[10] = ( float )( cr*cp );

}

inline void reset()
{
memset( m_matrix, 0, sizeof( float )*16 );
m_matrix[0] = m_matrix[5] = m_matrix[10] = m_matrix[15] = 1;
}

Matrix::Matrix()
{
reset();
}

##### Share on other sites

Either way, though, projection * view * model * position should be equal to projection * (view * (model * position)), because (as has been stated many times in this thread by everyone except JohnnyCode) matrix multiplication is associative.

I am stating from beginning of topic that the two formulas does not equal but you think they should becouse matrix multiplication is associative and vector is a matrix. By this you support your statement saying I am false. All I have written is totaly true and right

It got to the point you are telling me that I was not saying that matrix multiplication is associative as everyone was (like if I didn't know that), becouse I apointed that second formula tranposes the operation for the OP, and that the formulas does not equal.

There are so many posts of not only yours full of bolocks now that I refuse to deal with this anymore.

Yes it does, or, to be more to the point:
projection * (view * (model * position)) = ((projection * view) * model) * position

The vector transformation IS a matrix multiplication. As Bacterius said, associativity holds for all sizes,

Matrix multiplication is associative. For any three matrices A, B, C,

##### Share on other sites

JohnnyCode, it's not that people are angry at you specifically, it's just that what you are saying is factually wrong. Matrix multiplication (and that includes vector multiplication because vectors are also matrices) is associative. In other words:

projection * (view * (model * position))

is the same as

projection * view * model * position

and is the same as

((projection * view) * model) * position

but it is (generally) not the same as multiplying them in another order, e.g. view * model * projection * position. This is what order means. Order does not mean the order of the multiplications within the brackets, which is irrelevant by associativity, it means whether you are doing left or right multiplication of the different pairs of matrices involved.

PERIOD. If you still deny this then you seriously need to open a linear algebra textbook (or even look it up online, or even do the math yourself to see they are always the same) because your knowledge of matrices and vectors is wrong and you are not helping the OP by giving incorrect information all over this thread (in addition to derailing it).

There are so many posts of not only yours full of bolocks now that I refuse to deal with this anymore.

Good, us too. Now go learn how matrix multiplication works before posting about it again, please.

Edited by Bacterius

##### Share on other sites

@polyfrag: your matrix code looks good after a (mostly cursory) look. Correct me if you think I've read your code wrong, but it appears that your Matrix class uses a column major basis vector convention and is laid out in memory in column major order, and as such the premultiplying you're doing in your shaders is correct.

Unfortunately I still can't answer why PVMv is occasionally giving different results than P(V(Mv)), though.

##### Share on other sites

I think Johnny was saying that if the matrices are row-major (DirectX style), the tranpose of column-major, then you would have to multiply in the opposite order.

##### Share on other sites

I think Johnny was saying that if the matrices are row-major (DirectX style), the tranpose of column-major, then you would have to multiply in the opposite order.

Yes, but the opposite order is: A*B*C ==opposite order ==> C*B*A!=(A*B)*C

There's no problem about someone not catching a mathematical rule, but it is really problematic if someone is proclaiming a wrong view of a mathematical rule in a public forum.  At least the rating system come for the rescue

##### Share on other sites
This topic is now closed to further replies.

• ### Forum Statistics

• Total Topics
627743
• Total Posts
2978894

• 10
• 10
• 21
• 14
• 14