• Advertisement
Sign in to follow this  

Very weird matrix behavior in GLSL

This topic is 2350 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi Everbody,

Just came across a very weird behavior with matrix multiplication in GLSL shaders.
Trying to break this very common line of code:

gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;


into


vec4 pos = gl_ModelViewMatrix * gl_Vertex;
gl_Position = gl_ProjectionMatrix * pos;


but resulting gl_Position that gets calculated is different in these two samples.

This looks very weird, as matrix multiplication operation is associative (A * B) * C = A * (B * C).

Anybody came across this before? Why does this happen and is there a way to fix it?

Thanks,
Ruben

Share this post


Link to post
Share on other sites
Advertisement
I had the same issue before but couldn't tell you what I did, I think I just don't use the one that works and any other calculations I have a separate variable. So if you need "pos" variable, then use it but just make sure your gl_Position is always the one that you have that works.

Share this post


Link to post
Share on other sites
Matrices aren't communitive. You MUST multiply the first two together first, then multiply that result into gl_Vertex (which is acting as a column matrix in this operation). To see this for yourself, try multiplying them the original way, then do it the way you're trying to do it CPU-side, and print the results to the console. You'll see different results. Doing it by hand may also help realize what's going on.

Share this post


Link to post
Share on other sites

Matrices aren't communitive. You MUST multiply the first two together first, then multiply that result into gl_Vertex (which is acting as a column matrix in this operation). To see this for yourself, try multiplying them the original way, then do it the way you're trying to do it CPU-side, and print the results to the console. You'll see different results. Doing it by hand may also help realize what's going on.


I'm not saying that matrix multiplication is commutative A * B = B * A is not true for matrices. But it is supposed to be associative, which means that for every given matrices A, B, C following statement is true: (A * B) * C = A * (B * C). In my case A can be treated as gl_ProjectionMatrix, B - as gl_ModelViewMatrix, and C - as a gl_Vertex. Quote from wiki: http://en.wikipedia.org/wiki/Matrix_multiplication#Properties

I understand that there is something happening on the GPU side, but from math perspective, no matter what is the sequence of operations, result should be the same. I just did a test with Matlab with various random matrices/vectors and results were same.

Share this post


Link to post
Share on other sites
Try:

vec4 pos = gl_ModelViewMatrix * gl_Vertex;
gl_Position = gl_ProjectionMatrix * vec4(pos.xyz, 1.0);

Thats the only thing I can think of is something with the w component. Regardless if you need:

vec4 pos = gl_ModelViewMatrix * gl_Vertex;
//Do some calculations with pos variable
gl_Position = gl_ProjectionMatrix * pos;

You would have the same exact amount of lines of code by doing:

vec4 pos = gl_ModelViewMatrix * gl_Vertex;
//Do some calculations with pos variable
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;

so just do that anyway.

Share this post


Link to post
Share on other sites

Try:

vec4 pos = gl_ModelViewMatrix * gl_Vertex;
gl_Position = gl_ProjectionMatrix * vec4(pos.xyz, 1.0);

Thats the only thing I can think of is something with the w component. Regardless if you need:

vec4 pos = gl_ModelViewMatrix * gl_Vertex;
//Do some calculations with pos variable
gl_Position = gl_ProjectionMatrix * pos;

You would have the same exact amount of lines of code by doing:

vec4 pos = gl_ModelViewMatrix * gl_Vertex;
//Do some calculations with pos variable
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;

so just do that anyway.




Trick with setting w = 1.0 does not make any difference. I know that I can use gl_ModelViewProjectionMatrix instead, but that makes it very weird and I do not have any confidence when using matrix multiplication any more.... :(


Share this post


Link to post
Share on other sites
Trying to break this very common line of code:
gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;into vec4 pos = gl_ModelViewMatrix * gl_Vertex;
gl_Position = gl_ProjectionMatrix * pos;
Shouldn't you be concatenating the matrices, and then using the result to transform the position?vec4 modelViewProj = gl_ProjectionMatrix * gl_ModelViewMatrix;
gl_Position = modelViewProj * gl_Vertex;
[edit]actually, no, I don't see a problem with your method... transforming a point into view-space, and then projecting it should work too... :/

Share this post


Link to post
Share on other sites

[quote name='rubenhak' timestamp='1315637398' post='4859902']Trying to break this very common line of code:
gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;into vec4 pos = gl_ModelViewMatrix * gl_Vertex;
gl_Position = gl_ProjectionMatrix * pos;
Shouldn't you be concatenating the matrices, and then using the result to transform the position?vec4 modelViewProj = gl_ProjectionMatrix * gl_ModelViewMatrix;
gl_Position = modelViewProj * gl_Vertex;
[edit]actually, no, I don't see a problem with your method... transforming a point into view-space, and then projecting it should work too... :/
[/quote]

That could be another way of doing, but still cannot see a reason why doing my way does not work...

Share this post


Link to post
Share on other sites
This looks very weird, as matrix multiplication operation is associative (A * B) * C = A * (B * C).

Except that they are talking about matrix multiplcation. That is, C is not a vector when they write that.

The meaning is that (A * B) * C will create the same matrix as A * (B * C).
Using (gl_ModelViewProjectionMatrix * gl_Vertex) is the same as using (gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex) because the result of (gl_ProjectionMatrix * gl_ModelViewMatrix) is a matrix equal to gl_ModelViewProjectionMatrix.


L. Spiro

Share this post


Link to post
Share on other sites

[quote name='rubenhak' timestamp='1315637398' post='4859902']This looks very weird, as matrix multiplication operation is associative (A * B) * C = A * (B * C).

Except that they are talking about matrix multiplcation. That is, C is not a vector when they write that.

The meaning is that (A * B) * C will create the same matrix as A * (B * C).
Using (gl_ModelViewProjectionMatrix * gl_Vertex) is the same as using (gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex) because the result of (gl_ProjectionMatrix * gl_ModelViewMatrix) is a matrix equal to gl_ModelViewProjectionMatrix.


L. Spiro
[/quote]

Vector is a custom case of a m-by-n matrix where m (or n) is equal to 1.

I need to calculate value of pos:
pos = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;


I just want to do it differently:
p1 = gl_ModelViewMatrix * gl_Vertex;
p2 = gl_ProjectionMatrix * p1;


resulting p2 is not equal to pos. Could you explain me why? Manually I substitute any random values into gl_ProjectionMatrix, gl_ModelViewMatrix and gl_Vertex values and calculate pos and p2 manually and get equal results. However, in GLSL getting different values. Any ideas why?

Share this post


Link to post
Share on other sites
if you use opencl or cuda, or some other method to get actual values then u would be alright., i mean one method would be to create 4 buffers for an fbo. and draw a triangle

gl_Position = ftransform()

but send to your pixel shader vec4 (colors) of your final p2.xyzw into buffer 1, write the intermediate p1.xyzw into buffer 2, etc and then you can investigate the numbers.


Except that they are talking about matrix multiplcation. That is, C is not a vector when they write that.[/quote]
Right, but what we are all getting at is:

[projectionMatrix]*[Vector] what if Vector is pre-transformed by 20 matrix translations: it will still compute to a vector in world space that will still be allowed to be projected on the screen by a projection matrix. I can put Vector = 20,20,20,1 or I can call glTranslate(1,1,1) 19 times on a vector = 1,1,1,1, which makes it 20,20,20,1 and then project it after that and be the same result.
which looks like:
[projection][m1][m2]........[m19][vector]
[projection][translated vector], same result, and you can do the math on it, it should be the same result.


Trick with setting w = 1.0 does not make any difference.[/quote]
I'm just guessing, I went through the same thing and just did what I posted. It is obviously doing something, but you have to debug and get the numbers. Actually now that I think of it, gl_ModelViewProjectionMatrix might be stored different, I think that gl_ProjectionMatrix on its own is f'd up, just debug the numbers and compare because if you even do this:

my_glMViewProjection = gl_ProjectionMatrix*gl_ModelViewMatrix; // not equal to: gl_ModelviewProjectionMatrix;
I believe that does not even work. Try it out.

Share this post


Link to post
Share on other sites
Try sending the matrices yourself instead of using the built-in ones.
Try pos * gl_ProjectionMatrix instead of gl_ProjectionMatrix * pos.


L. Spiro

Share this post


Link to post
Share on other sites


Vector is a custom case of a m-by-n matrix where m (or n) is equal to 1.

I need to calculate value of pos:
pos = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;


I just want to do it differently:
p1 = gl_ModelViewMatrix * gl_Vertex;
p2 = gl_ProjectionMatrix * p1;


resulting p2 is not equal to pos. Could you explain me why? Manually I substitute any random values into gl_ProjectionMatrix, gl_ModelViewMatrix and gl_Vertex values and calculate pos and p2 manually and get equal results. However, in GLSL getting different values. Any ideas why?


[/quote]

Pass the matrices as uniforms and then do the computation. I'm using version 410 and GLM math to create the matrices and I have no issues if I use intermediate variables. Of course, I'm not using the built-in types.

Share this post


Link to post
Share on other sites

if you use opencl or cuda, or some other method to get actual values then u would be alright., i mean one method would be to create 4 buffers for an fbo. and draw a triangle

gl_Position = ftransform()

but send to your pixel shader vec4 (colors) of your final p2.xyzw into buffer 1, write the intermediate p1.xyzw into buffer 2, etc and then you can investigate the numbers.


Except that they are talking about matrix multiplcation. That is, C is not a vector when they write that.

Right, but what we are all getting at is:

[projectionMatrix]*[Vector] what if Vector is pre-transformed by 20 matrix translations: it will still compute to a vector in world space that will still be allowed to be projected on the screen by a projection matrix. I can put Vector = 20,20,20,1 or I can call glTranslate(1,1,1) 19 times on a vector = 1,1,1,1, which makes it 20,20,20,1 and then project it after that and be the same result.
which looks like:
[projection][m1][m2]........[m19][vector]
[projection][translated vector], same result, and you can do the math on it, it should be the same result.


Trick with setting w = 1.0 does not make any difference.[/quote]
I'm just guessing, I went through the same thing and just did what I posted. It is obviously doing something, but you have to debug and get the numbers. Actually now that I think of it, gl_ModelViewProjectionMatrix might be stored different, I think that gl_ProjectionMatrix on its own is f'd up, just debug the numbers and compare because if you even do this:

my_glMViewProjection = gl_ProjectionMatrix*gl_ModelViewMatrix; // not equal to: gl_ModelviewProjectionMatrix;
I believe that does not even work. Try it out.
[/quote]

All the mystery is that even with my_glMViewProjection calculation is correct. Let me bring some facts:

1) The goal is to compute (not willing to use gl_ModelViewProjectionMatrix at all):
[color=#1C2837][size=2]pos = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;
[color=#1C2837][size=2]

[color=#1C2837][size=2]2) Flowing computes correct value: r1 equals pos
[color=#1C2837][size=2]mat4 mat = [color=#1C2837][size=2]gl_ProjectionMatrix * [color=#1C2837][size=2]gl_ModelViewMatrix;
[color=#1C2837][size=2]r1 = mat * gl_Vertex;
[color=#1C2837][size=2]

[color=#1C2837][size=2]3) These statements compute r2 which is different from r1 and pos
[color=#1C2837][size=2]p1 = gl_ModelViewMatrix * gl_Vertex;[color=#1C2837][size=2]r2 = gl_ProjectionMatrix * p1;

Share this post


Link to post
Share on other sites

Try sending the matrices yourself instead of using the built-in ones.
Try pos * gl_ProjectionMatrix instead of gl_ProjectionMatrix * pos.


L. Spiro


Tried all possible postfix/prefix multiplications together with transposing the matrix, but still, cannot get the final result.

Share this post


Link to post
Share on other sites



Vector is a custom case of a m-by-n matrix where m (or n) is equal to 1.

I need to calculate value of pos:
pos = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;


I just want to do it differently:
p1 = gl_ModelViewMatrix * gl_Vertex;
p2 = gl_ProjectionMatrix * p1;


resulting p2 is not equal to pos. Could you explain me why? Manually I substitute any random values into gl_ProjectionMatrix, gl_ModelViewMatrix and gl_Vertex values and calculate pos and p2 manually and get equal results. However, in GLSL getting different values. Any ideas why?




Pass the matrices as uniforms and then do the computation. I'm using version 410 and GLM math to create the matrices and I have no issues if I use intermediate variables. Of course, I'm not using the built-in types.
[/quote]


Will look into GLM. But still it should not be any different cause with just changing order of computation in GLSL shader results in different values.

How happy are you with GLM? I'm thinking about replacing my custom made math library with a robust opensource one. Yet not decided which library to pick.

Share this post


Link to post
Share on other sites
[color=#1C2837][size=2]1) The goal is to compute (not willing to use gl_ModelViewProjectionMatrix at all):
[color=#1C2837][size=2]pos = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;[/quote]
[color=#1C2837][size=2][color=#1C2837][size=2]

[color=#1C2837][size=2][color=#1C2837][size=2]What I keep telling you is that your goal is dumb. Its broke, we don't know why. Can you still carry on and make a shader: yes. Then do it. How else are you going to calculate pos? you need to multiply the vertex into camera space and onto the screen. One way or another your going to have a line of code to do that. Either use pos = ftransform(), pos = glmodelviewprojection*glvertex, or send in your own modelviewprojectionmatrix.
[color=#1C2837][size=2][color=#1C2837][size=2]

[color=#1C2837][size=2][color=#1C2837][size=2]Are you seriously preferring this:
[color=#1C2837][size=2]pos = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;
[color=#1C2837][size=2]to this:
[color=#1C2837][size=2]pos = gl_ModelViewProjectionMatrix * gl_Vertex;
[color="#1c2837"]Your doing extra computational work.

Share this post


Link to post
Share on other sites

[color="#1C2837"]1) The goal is to compute (not willing to use gl_ModelViewProjectionMatrix at all):
[color="#1C2837"]pos = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;

[color="#1C2837"][color="#1C2837"]
[color="#1C2837"][color="#1C2837"]What I keep telling you is that your goal is dumb. Its broke, we don't know why. Can you still carry on and make a shader: yes. Then do it. How else are you going to calculate pos? you need to multiply the vertex into camera space and onto the screen. One way or another your going to have a line of code to do that. Either use pos = ftransform(), pos = glmodelviewprojection*glvertex, or send in your own modelviewprojectionmatrix.
[color="#1C2837"][color="#1C2837"]
[color="#1C2837"][color="#1C2837"]Are you seriously preferring this:
[color="#1C2837"]pos = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;
[color="#1C2837"]to this:
[color="#1C2837"]pos = gl_ModelViewProjectionMatrix * gl_Vertex;
[color="#1c2837"]Your doing extra computational work.
[/quote]

In this particular thread I'm not trying to get and answer to answer on how to perform projection transformation. Before opening this thread I knew that I could do "[color=#1C2837][size=2]pos = gl_ModelViewProjectionMatrix * gl_Vertex;" and it would work like a charm. But I want to get and answer why does the order of calculation make difference, and for that particular reason I keep using this construct "[color=#1C2837][size=2]pos = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;" just to concentrate on multiplication.
[color=#1C2837][size=2]

[color=#1C2837][size=2]I have some bigger trouble related with matrix multiplication, and I believe that its because of exactly same problem as described in this thread.
[color=#1C2837][size=2]

[color=#1C2837][size=2]Does it make a bit of sense?

Share this post


Link to post
Share on other sites

[quote name='dpadam450' timestamp='1315851819' post='4860794']
[color="#1C2837"]1) The goal is to compute (not willing to use gl_ModelViewProjectionMatrix at all):
[color="#1C2837"]pos = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;


[color="#1C2837"][color="#1C2837"]What I keep telling you is that your goal is dumb. Its broke, we don't know why. Can you still carry on and make a shader: yes. Then do it. How else are you going to calculate pos? you need to multiply the vertex into camera space and onto the screen. One way or another your going to have a line of code to do that. Either use pos = ftransform(), pos = glmodelviewprojection*glvertex, or send in your own modelviewprojectionmatrix.

[color="#1C2837"][color="#1C2837"]Are you seriously preferring this:
[color="#1C2837"]pos = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;
[color="#1C2837"]to this:
[color="#1C2837"]pos = gl_ModelViewProjectionMatrix * gl_Vertex;
[color="#1c2837"]Your doing extra computational work.
[/quote]


In this particular thread I'm not trying to get and answer to answer on how to perform projection transformation. Before opening this thread I knew that I could do "[color="#1C2837"]pos = gl_ModelViewProjectionMatrix * gl_Vertex;" and it would work like a charm. But I want to get and answer why does the order of calculation make difference, and for that particular reason I keep using this construct "[color="#1C2837"]pos = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;" just to concentrate on multiplication.

[color="#1C2837"]I have some bigger trouble related with matrix multiplication, and I believe that its because of exactly same problem as described in this thread.

[color="#1C2837"]Does it make a bit of sense?
[/quote]


Yes, it makes sense. I understand you're not concerned with efficiency at all. You're just wondering why GLSL is not preserving associativity when you use the built-in types. In that sense, you are not really changing the order of the multiplication but you are changing the grouping. If you were changing order, then we know matrix multiplication is non-commutative and that would be the end of it. But you are wondering why associativity of multiplication is not preserved when it should. BTW, GLM seems to me as a good replacement for the old mathematical fixed functions like gluLookAt, et al. It uses a syntax that is similar to GLSL like vec3, mat4, etc...

Share this post


Link to post
Share on other sites

Yes, it makes sense. I understand you're not concerned with efficiency at all. You're just wondering why GLSL is not preserving associativity when you use the built-in types. In that sense, you are not really changing the order of the multiplication but you are changing the grouping. If you were changing order, then we know matrix multiplication is non-commutative and that would be the end of it. But you are wondering why associativity of multiplication is not preserved when it should. BTW, GLM seems to me as a good replacement for the old mathematical fixed functions like gluLookAt, et al. It uses a syntax that is similar to GLSL like vec3, mat4, etc...


Yeah, that's what i meant - "grouping" thanks for correction.

I've got my own implementations of LookAt, projection and rest of GL helper library functions so not worried about those much. I'm just thinking if GLM could be a good candidate for replacement, as it could be more robust and better tested by community than my own implementation of math lib.

Does GLM support templated data types? For example, can I use vec3<float>, or vec3<double> ? Does it have wrappers for trigonometric functions?


Share this post


Link to post
Share on other sites

[quote name='Jesse7' timestamp='1315857744' post='4860843']
Yes, it makes sense. I understand you're not concerned with efficiency at all. You're just wondering why GLSL is not preserving associativity when you use the built-in types. In that sense, you are not really changing the order of the multiplication but you are changing the grouping. If you were changing order, then we know matrix multiplication is non-commutative and that would be the end of it. But you are wondering why associativity of multiplication is not preserved when it should. BTW, GLM seems to me as a good replacement for the old mathematical fixed functions like gluLookAt, et al. It uses a syntax that is similar to GLSL like vec3, mat4, etc...


Yeah, that's what i meant - "grouping" thanks for correction.

I've got my own implementations of LookAt, projection and rest of GL helper library functions so not worried about those much. I'm just thinking if GLM could be a good candidate for replacement, as it could be more robust and better tested by community than my own implementation of math lib.

Does GLM support templated data types? For example, can I use vec3<float>, or vec3<double> ? Does it have wrappers for trigonometric functions?

[/quote]

I am new to this library and haven't tested it thoroughly. There seems to be classes that start with a "t" that support templates, for example tvec3 < T >, and it does wrap trig functions.

http://glm.g-truc.ne.../annotated.html

Share this post


Link to post
Share on other sites

[quote name='rubenhak' timestamp='1315859214' post='4860854']
[quote name='Jesse7' timestamp='1315857744' post='4860843']
Yes, it makes sense. I understand you're not concerned with efficiency at all. You're just wondering why GLSL is not preserving associativity when you use the built-in types. In that sense, you are not really changing the order of the multiplication but you are changing the grouping. If you were changing order, then we know matrix multiplication is non-commutative and that would be the end of it. But you are wondering why associativity of multiplication is not preserved when it should. BTW, GLM seems to me as a good replacement for the old mathematical fixed functions like gluLookAt, et al. It uses a syntax that is similar to GLSL like vec3, mat4, etc...


Yeah, that's what i meant - "grouping" thanks for correction.

I've got my own implementations of LookAt, projection and rest of GL helper library functions so not worried about those much. I'm just thinking if GLM could be a good candidate for replacement, as it could be more robust and better tested by community than my own implementation of math lib.

Does GLM support templated data types? For example, can I use vec3<float>, or vec3<double> ? Does it have wrappers for trigonometric functions?

[/quote]

I am new to this library and haven't tested it thoroughly. There seems to be classes that start with a "t" that support templates, for example tvec3 < T >, and it does wrap trig functions.

http://glm.g-truc.ne.../annotated.html


[/quote]


looks promising. Will upgrade to GLM overnight today :)

Share this post


Link to post
Share on other sites
is this a problem you are having on nvidia or amd? on windows or linux or ...?

Share this post


Link to post
Share on other sites

is this a problem you are having on nvidia or amd? on windows or linux or ...?


windows 7 64 bit, nvidia gtx 480.

I suspect i have same issue with iOS

Share this post


Link to post
Share on other sites
I actually put your original problem (and the one i had some years ago) into a shader of mine real quick and it worked fine. I got a GTS 450 and I had updated my driver a couple days ago. Try updating driver.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement