opentk / LearnOpenTK

A port of learnopengl.com's tutorials to OpenTK and C#.
Creative Commons Attribution 4.0 International
463 stars 115 forks source link

GLSL typically uses column-major,but the demo shader of "7-Transformations" use pre-multiplication #94

Closed ylu0105 closed 4 months ago

ylu0105 commented 4 months ago

GL.UniformMatrix4 is a fairly standard function, similar to the other Uniform functions we've seen so far. The parameters are as follows:

The location of the uniform on the shader. A boolean, determining whether or not the matrices should be transposed. Since OpenTK uses row-major, whereas GLSL typically uses column-major, you'll almost always want to use true here. A reference to the matrix we want to pass.]

ylu0105 commented 4 months ago

I found that although matrices in OpenTK are displayed and operated on according to row-major, elements in memory are stored according to column-major.Therefore, there is no need to transpose when passing to the shader.

NogginBops commented 4 months ago

The reason you want to set transpose to true is to get consistent matrix multiplication order. In C# the correct matrix multiplication is position * model * view * projection, if you don't set transpose=true you would have to do projection * view * model * position in your shader, but if you set it to true you get consistent position * model * view * projection in both C# and glsl.

ylu0105 commented 4 months ago

The reason you want to set transpose to true is to get consistent matrix multiplication order. In C# the correct matrix multiplication is position * model * view * projection, if you don't set transpose=true you would have to do projection * view * model * position in your shader, but if you set it to true you get consistent position * model * view * projection in both C# and glsl.

if you just want to stay consistent matrix multiplication order in C# and GLSL.You shouldn't transpose the matrix!As is said above,Matrix4 in OpenTK are displayed and operated according to row-major, but the elements in memory are stored according to column-major.This should be a bug! @NogginBops The only scenario that needs to be transposed is when the multiplication order is reversed

ylu0105 commented 4 months ago

Matrix4 trans = Matrix4.CreateTranslation(1f, 1f, 0.0f); unsafe { Matrix4 address = &trans; float address2 = (float*)address; for (int i = 0; i < 16; i++) { Console.WriteLine(address2[i]); } } Row-major memory layout: image Column-major memory layout: image

Please pay attention to the tx,ty,tz position!

NogginBops commented 4 months ago

OpenTK matrices are stored in row-major order, and vector matrix multiplication does a row-vector matrix multiplication.

I think the confusion comes from the multiplication convention where we have opted for left-to-right multiplication order for matrix multiplications. Every function in OpenTK that creates does so with the assumption that the matrix is going to be multiplied left-to-right. For example translation matrices are stored like this in OpenTK: image

Which happens to line up with a right-to-left column major representation, but the result of chained matrix multiplication is different.

ylu0105 commented 4 months ago

I understand every matrices(vectors) in openTK are in row-major order. But the save order in memory is incorrect!

image

For example: The above matrix is stored in memory as : 1,0,0,0,0,1,0,0,0,0,1,0,tx,ty,tz,1 But it was a column-major memory layout in OpenGL(GLSL): image

utkumaden commented 4 months ago

Incorrect according to whom? Storing row vectors is as valid as storing column vectors. Just because the math library conventions are different from what you are used to does not mean it is incorrect.

There is some history behind the choice and why it has been kept that way: DirectX used to prefer row vectors and as such the library was designed that way by the original authors. Since OpenGL permits both row major and column major matrices, this was a fine choice.

Nowadays changing this order would yield in breaking so many libraries, their code and cause us many more complaints than the number of people that complain about the current state.

If it is such a problem use System.Numerics matrices and pass matrices using the ref float overloads.

ylu0105 commented 4 months ago

Incorrect according to whom? Storing row vectors is as valid as storing column vectors. Just because the math library conventions are different from what you are used to does not mean it is incorrect.

There is some history behind the choice and why it has been kept that way: DirectX used to prefer row vectors and as such the library was designed that way by the original authors. Since OpenGL permits both row major and column major matrices, this was a fine choice.

Nowadays changing this order would yield in breaking so many libraries, their code and cause us many more complaints than the number of people that complain about the current state.

If it is such a problem use System.Numerics matrices and pass matrices using the ref float overloads.

OK!

NogginBops commented 4 months ago

The above matrix is stored in memory as : 1,0,0,0,0,1,0,0,0,0,1,0,tx,ty,tz,1 But it was a column-major memory layout in OpenGL(GLSL):

No, we are storing every row, one after another. It truly is row major in memory. It's just that we have a different matrix convention that places elements of the matrix in different places than what you would see in "normal" math. This happens to align with a right-to-left convention column-major matrix.

System.Numerics uses the exact same right-to-left row-major matrices. And will have the exact same memory layout as OpenTK. You can read more about it here: https://stackoverflow.com/a/17718408