• Advertisement
Sign in to follow this  

Rotation matrix stretches geometry

This topic is 872 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

Hello everyone,

 

 

I used to use collumn major perspective projection and row major translation matrix because it was not working with column major translation even though my multiplication in shader looks like this:

  • gl_Position = projection * translation * rotation * object

Which as I understand it is collumn major ordering. But somewhy it works only with row major matrixes that I pass to shader. So I switched to row major perspective and left the same with translation. Translation works as it should but somewhy rotation matrix stretches the triangle.

[sharedmedia=core:attachments:29807]

 

 

The code for rotation is here:

In main function I use function like this -> GLXMatrixRotate(&view, angle, 1.0, 1.0, 1.0); which rotates aroundd x, y, z axis

GLXMATRIX* GLXMatrixRotate(GLXMATRIX* pOut, float angle, float x, float y, float z) {

	const float x2 = x*x;
	const float y2 = y*y;
	const float z2 = z*z;
	float rads = angle  * (PI / 180.0f);
	const float c = cosf(rads);
	const float s = sinf(rads);
	const float omc = 1.0f - c;

	pOut->m[0][0] = x2 * omc + c;
	pOut->m[0][1] = y * x * omc + z * s;
	pOut->m[0][2] = x * z * omc - y * s;

	pOut->m[1][0] = x * y * omc - z * s;
	pOut->m[1][1] = y2 * omc + c;
	pOut->m[1][2] = y * z * omc + x * s;

	pOut->m[2][0] = x * z * omc + y * s;
	pOut->m[2][1] = y * z * omc - x * s;
	pOut->m[2][2] = z2 * omc + c;
	return pOut;
}

Does anyone know the problem?

Edited by renderkid

Share this post


Link to post
Share on other sites
Advertisement

Aren't your multiplications in the wrong order?

I always do mine in the order "ISROT":

* Identity Matrix
* Scale Matrix
* Rotation Matrix
* Orbit Matrix
* Translate Matrix

You're doing projection, translation, rotation?

Let me know if this helps...

 

Projection is perspective matrix, translation matrix is scale, rotation holds rotation matrix and object is object space.

GLXMatrixRotate is stored in rotation matrix.

I'm going to check it.

The math is taken from here -> https://github.com/openglsuperbible/sb7code/blob/master/include/vmath.h

Edited by renderkid

Share this post


Link to post
Share on other sites


Which as I understand it is collumn major ordering. But somewhy it works only with row major matrixes that I pass to shader

There's quirk in OpenGL, which has roots in its predecessor - Iris GL.

Even if matrices is column-major, their memory layout is exactly the same as if they were row-major.

So either transpose it during uniform setup (see third argument to glUniformMatrix), or transpose it manually before uploading.

Share this post


Link to post
Share on other sites

 


Which as I understand it is collumn major ordering. But somewhy it works only with row major matrixes that I pass to shader

There's quirk in OpenGL, which has roots in its predecessor - Iris GL. Because I was wondering why GLM uses row major matrix and multiplication major collumn works even though it should not...

Even if matrices is column-major, their memory layout is exactly the same as if they were row-major.

So either transpose it during uniform setup (see third argument to glUniformMatrix), or transpose it manually before uploading.

 

Well that's strange... Never knew about it. Thanks for clarification. I always was thinking why GLM uses row major matrixes and why it works when I multiply in shader in column major ordering when it's getting row major matrices

Edited by renderkid

Share this post


Link to post
Share on other sites

Mathematically, there's no such thing as a row/column major matrix. A matrix is a matrix and the rules will remain the same. E.g. A*B != B*A. Don't do your matrix multiplications in different orders to get the expected results, do them in the correct order. If you are doing rotation THEN translation THEN projection you need to do it in this order: projection*translation*rotation.

 

That said.. computationally there is such a thing as row/column major matrices as you have pointed out. As long as all the matrices are the same ordering then the maths will work just fine, the issue occurs when one is a row major and the other is a column major as multiplying those is like multiplying a matrix by the transpose of another. So choose one and stick to it. OpenGL uses column-major so I recommend that. If you are using row major matrices then transpose them just before setting your uniform as Vstrakh said. Once in your shader a matrix is a matrix so you shouldn't be worrying about row/column major here just make sure they are being loaded correctly as column-major first and the matrix operations will work as you expect them too.

 

With regard to your scaling when rotating, have you tried it with eliminating the other matrices first? I.e. having only the projection matrix (since you kinda need that..) and dropping the object/translation matrices. One way to check if your rotation matrix is ok is that all 3 basis are at right angles to each other and of unit length. I'm somewhat rusty on how to get those basis vectors, they are either the first 3 rows or the first 3 columns depending on ordering. you can then work out their lengths (should be 1) and you can dot product them individually (should give 0).

 

I'm slightly rusty on that though so I'm open to being corrected on that.

Share this post


Link to post
Share on other sites

Nanoha, it seems that rotation works fine. I tried to do several test with row ordering and major ordering.

I firstly tried to display simple triangle on the axis -1. 

Projection matrix Projection(65.0f, 800.0f/600.0f, 0.1f, 100.0f)
1.177264 0.000000 0.000000 0.000000 
0.000000 1.569686 0.000000 0.000000 
0.000000 0.000000 -1.002002 -0.200200 
0.000000 0.000000 -1.000000 0.000000 

Translation matrix
1.000000 0.000000 0.000000 0.000000 
0.000000 1.000000 0.000000 0.000000 
0.000000 0.000000 1.000000 -1.000000 
0.000000 0.000000 0.000000 1.000000

In shader I multiplied them like this (not transposed): gl_Position = projection * translation * vec4(obj, 1.0);
But triangle was not showing. Tried all directions. It works fine if I apply this ordering:
gl_Position = vec4(obj, 1.0) * translation * projection.

Then I switched to row major matrices.
Projection matrix Projection(65.0f, 800.0f/600.0f, 0.1f, 100.0f)
1.177264 0.000000 0.000000 0.000000 
0.000000 1.569686 0.000000 0.000000 
0.000000 0.000000 -1.002002 -1.000000 
0.000000 0.000000 -0.200200 0.000000 

Translation matrix
1.000000 0.000000 0.000000 0.000000 
0.000000 1.000000 0.000000 0.000000 
0.000000 0.000000 1.000000 0.000000 
0.000000 0.000000 -1.000000 1.000000 

Multiplication in shader is collumn ordering (not transposed): gl_Position = projection * translation * vec4(obj, 1.0);
Now I was able to see the triangle.

I tested the rotation just passing rotation matrix to shader and nothing else. It does not stretch the geometry .But once I apply projection the problem with rotation persists.

But kinda confused right now

 

gl_Position = projection * translation * object -> is collumn ordering?

g_Position = object * translation * projection -> is row ordering?

Am I right with orderings?

Rotate(45, 1.0, 0.0, 0.0) rotates around x axis
1.000000 0.000000 0.000000 0.000000 
0.000000 0.707107 0.707107 0.000000 
0.000000 -0.707107 0.707107 0.000000 
0.000000 0.000000 0.000000 1.000000 

Rotate(45, 0.0, 1.0, 0.0) rotates around y axis
0.707107 0.000000 -0.707107 0.000000 
0.000000 1.000000 0.000000 0.000000 
0.707107 0.000000 0.707107 0.000000 
0.000000 0.000000 0.000000 1.000000 

Rotate(45, 0.0, 0.0, 1.0) rotates around z axis
0.707107 0.707107 0.000000 0.000000 
-0.707107 0.707107 0.000000 0.000000 
0.000000 0.000000 1.000000 0.000000 
0.000000 0.000000 0.000000 1.000000 
Edited by renderkid

Share this post


Link to post
Share on other sites

Is this your own Matrix class? I briefly Googled the name and didn't see it so I am assuming so. Would you might posting the code for the projection matrix creation? I only ask because I put the values you used in my own and I get hugely different values for [0, 0] and [1,1].

 

When you print/display a matrix it should look the same regardless of if it sits column or row major in memory. Similarly if you alter an element [c,r] it shouldn't' matter how it is arranged in memory, you should be changing the element at column c and row r. It would probably be a good idea to hide this away behind the operator()(int c, int r). That way your matrix itself doesn't change just because you change the column/row majoring.

 

 

As a 'general' translation matrix (https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations) your first translation matrix looks correct. Your rotations look good but they are transposed. https://en.wikipedia.org/wiki/Rotation_matrix That is to say they 'look' transposed.

 

There are some properties of matrices here https://en.wikipedia.org/wiki/Transpose#Properties number 3 might be coming in to effect somehow which makes them 'work' but I wouldn't like to rely on it.

 

I think it would help you a lot if you overloaded the () operator:

float& operator()(int column, int row);

such that when you do m(2, 3) it doesn't have a different effect if the matrix is laid out differently in memory. In a column-major matrix that would be element 11 but in a row major one in would be element 14. Overloading the [] operator doesn't play nice when you start to change row/column majoring. 

Share this post


Link to post
Share on other sites

Yup. Here it is. Matrixes at the first are set to identity.

//Collumn
GLXMATRIX* GLXMatrixPerspectiveCM(GLXMATRIX *pOut,	float fov, float aspect, float zn,	float zf) {
#ifdef TYW_DEBUG
	if (!pOut)return nullptr;
#endif
	float q = 1.0f / tan(0.5f*fov * (PI / 180.0f));
	float A = q / aspect;
	float B = (zn + zf) / (zn - zf);
	float C = (2.0f * zn * zf) / (zn - zf);

	pOut->m[0][0] = A;
	pOut->m[1][1] = q;
	pOut->m[2][2] = B;
	pOut->m[2][3] = C;
	pOut->m[3][2] = -1;
	pOut->m[3][3] = 0;
	return pOut;
}

//Row
GLXMATRIX* GLXMatrixPerspectiveRM(GLXMATRIX *pOut, float fov, float aspect, float zn, float zf) {
#ifdef TYW_DEBUG
	if (!pOut)return nullptr;
#endif
	float q = 1.0f/ tan(0.5f*fov * (PI / 180.0f));
	float A = q / aspect;
	float B = (zn + zf) / (zn - zf);
	float C = (2.0f * zn * zf) / (zn - zf);

	pOut->m[0][0] = A;
	pOut->m[1][1] = q;
	pOut->m[2][2] = B;
	pOut->m[2][3] = -1;
	pOut->m[3][2] = C;
	pOut->m[3][3] = 0;
	return pOut;
}

Edited by renderkid

Share this post


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

  • Advertisement