Rotation matrix multiplication causes Gimbal lock

Started by
23 comments, last by scragglypoo 6 years, 8 months ago

me.rotation = me.rotation * CreateRotation( 5.0, 0.0, 0.0 );

If i spawn and run this code, pitch increases

But if i spawn and turn 90deg left and run this code, it rolls

How do i fix this?

 

Edit: I use a standard rotation matrix formula from wikipedia

Advertisement

If you're using rotation matrices, there's not really a way to avoid gimbal lock. It happens because you're rotating one axis into another axis, effectively removing a degree of freedom.

The best solution (though not necessarily beginner-friendly) is to use quaternions to store your orientations. You can easily (relatively speaking) convert from matrix to quat, and back. I.e., calculate changes in orientation using quaternions, and then convert to matrix when you want to render, so that you can use the matrix to apply the appropriate transformation on demand.

(Honestly, quaternions are the kind of code you copy from somewhere else and apply based on faith.. they're not really the kind of thing you want to study the math theory behind, unless you're into that sort of thing)

Looks like a fundamental limitation in using Euler angles: https://en.wikipedia.org/wiki/Gimbal_lock#Loss_of_a_degree_of_freedom_with_Euler_angles

 

Suggested fix is "don't use Euler angles", not sure how feasible that is for you.

5 hours ago, masskonfuzion said:

If you're using rotation matrices, there's not really a way to avoid gimbal lock. It happens because you're rotating one axis into another axis, effectively removing a degree of freedom.

The best solution (though not necessarily beginner-friendly) is to use quaternions to store your orientations. You can easily (relatively speaking) convert from matrix to quat, and back. I.e., calculate changes in orientation using quaternions, and then convert to matrix when you want to render, so that you can use the matrix to apply the appropriate transformation on demand.

(Honestly, quaternions are the kind of code you copy from somewhere else and apply based on faith.. they're not really the kind of thing you want to study the math theory behind, unless you're into that sort of thing)

I have to use matrices to store the rotation

me.orientation = (Quaternion( me.rotation ) * CreateQuaternion( 5.0, 0.0, 0.0 )).toMatrix();

i tried this code and i get the same result

5 hours ago, Alberth said:

Looks like a fundamental limitation in using Euler angles: https://en.wikipedia.org/wiki/Gimbal_lock#Loss_of_a_degree_of_freedom_with_Euler_angles

 

Suggested fix is "don't use Euler angles", not sure how feasible that is for you.

How do I convert 5 degrees pitch to a non-euler method?

I don't think the issue has to do with the matrix representation, and it certainly doesn't seem to be gimbal lock to me. Have you tried multiplying the matrices the other way around?

me.rotation = CreateRotation( 5.0, 0.0, 0.0 ) * me.rotation;

On 7/21/2017 at 10:56 PM, alvaro said:

I don't think the issue has to do with the matrix representation, and it certainly doesn't seem to be gimbal lock to me. Have you tried multiplying the matrices the other way around?

 



me.rotation = CreateRotation( 5.0, 0.0, 0.0 ) * me.rotation;

 

If I reverse the order, pitch rotations are correct, but then yaw rotations decrease the pitch to 0

If I reverse order again, yaw rotations work again, but the pitch rolls if I turned 90deg left

 

I can make a weird fix by doing this:
it works for either operand order as well.  But why?


Matrix operator * ( const Matrix& rotation ) {
	auto x = CreateRotation( rotation.Pitch(), 0.0, 0.0 );
	auto y = CreateRotation( 0.0, rotation.Yaw(), 0.0 );
	return x * (*this) * y;
}

 

If you want to keep a constant up/down direction (ie gravity) then here's a simple trick I use:  First, be sure to reset your rotation every frame.  Then perform pitch (x) rotation first.  Then do your roll (z) rotation in local space.  And then perform the yaw (y-rotation) in world space.

I guess I'm not sure what the transformation pipeline looks like in this scenario.  @scragglypoo, what library are you using to make your game/project?

On 7/22/2017 at 3:58 PM, masskonfuzion said:

I guess I'm not sure what the transformation pipeline looks like in this scenario.  @scragglypoo, what library are you using to make your game/project?

Reversing a very old game.  I can try to show what I can figure out.


eye = me.position;
rotationProjectionMatrix = me.rotation;

// This is not standard vector multiplication?  
// Note: It uses a vector instead of a matrix for projection.
rotationProjectionMatrix.right *= projectionVector.x;
rotationProjectionMatrix.up *= projectionVector.y;
rotationProjectionMatrix.look *= projectionVector.z;

// Projection:
screen = mesh.position - eye;
screen *= rotationProjectionMatrix; // screen = local_position * rotation * projection;

 

Thanks for sharing some code.  It helps give me perspective (pun not intended :-D).  Unfortunately, I'm not following the code -- it looks to be very non-standard/customized, in terms of what I'd expect to see in a 3D application.

For starters, the name, "rotationProjectionMatrix" is confusing.  Rotation and projection are two very different kinds of transformation.  Without getting into too much "math speak", rotation simply transforms axes to point in different directions; it does not "deform" the relationships among the axes (i.e., after any rotation, the X, Y, and Z axes stay at 90 degree angles with respect to each other).

On the other hand, projections are not guaranteed to preserve that property (especially not perspective projections, which "squash" from 3D onto 2D, removing an entire dimension/axis/coordinate space).  That's why, e.g., OpenGL treats the "model/view" transformation matrix (which includes rotation) as an entirely separate entity from the "projection" matrix.  In OpenGL, you apply all of your model/view transformations first (rotation/translation/scaling/shear/whatever) with the "MODELVIEW" matrix.  Then after that, you use the "PROJECTION" matrix to flatten your transformed 3D geometry into a convincing 2D image, for display on whatever screen/monitor you're using (if you didn't know that, now you do :-D).

Based on your code, the "rotationProjectionMatrix" looks not like a matrix, but like a camera object.  It has an eye position vector, view space basis vectors (up/look/right) and a projection "object" -- you called the projection object a vector, but I contend that it should be a matrix, because projection is a transformation that really can only be done with a matrix.

This series of articles might be useful: http://www.scratchapixel.com/lessons/3d-basic-rendering/perspective-and-orthographic-projection-matrix/projection-matrix-introduction  <-- but it gets very heavy into math; I had to read and re-read it 4 or 5 times before it clicked

Lastly, you seem to have 2 "lookAt" vectors.. The object named "vector" appears to serve the same purpose that the rotationProjectionMatrix.look vector should serve.  Here's how OpenGL does it: https://www.khronos.org/opengl/wiki/GluLookAt_code

Whew.. This turned into a long post. Does this make sense?

This topic is closed to new replies.

Advertisement