Prevent rotation around certain axis with Quaternion - c++

I'm programming camera controls with a mouse. I want the user to be able to look up and down or left and right but not be able to "tilt" to the side. Put another way, I want to allow Yaw and Pitch to change but restrict Roll to 0.
How can I prevent a Quaternion from rotating around a certain axis? When I only allow x-axis rotation or y-axis rotation by themselves, everything works fine, but when I allow both at the same time for some reason the y-rotation is affected and the camera tilts to the side.

As CNuts explained, concatenating multiple yaw and pitch rotations can result in a roll rotation. However, this does not occur if the yaw and pitch rotations are strictly separated in the multiplication order. E.g.
Yaw1 * Yaw2 * Yaw3 * Pitch1 * Pitch2
There will be problems only when another yaw rotation is added to the end. But you can easily prevent this: If you want to add a yaw rotation, multiply it to the left. If you want to add a pitch rotation, multiply it to the right. I.e. if the current camera rotation is R, then:
R = additionalYaw * R
or
R = R * additionalPitch
Since all the yaw rotations are about the same axis, these transforms actually commute. So it does not matter in what order they appear (same applies to all other rotations about the same axis). Of course, you can also switch the order (i.e. pitch first, then yaw). Depending on what kind of control you want.
CNuts also described an alternative approach. If you have a transform hierarchy, you can have two nodes for your camera transform. Then, add yaw rotations to one of them and pitch rotations to the other. This will have the same effect as described above, effectively separating rotations about different axes.

Related

How to flip only one axis of transformation matrix?

I have a 4x4 transformation matrix. However, after trying out the transformation I noticed that movement and rotation of the Y axis is going the opposite way. The rest is correct.
I got this matrix from some other API so probably it is the difference of coordinate system. So, how can I flip an axis of transformation matrix?
If only translation I can add minus sign on the Y translation, but I have no idea about opposite rotation of only one axis since all the rotation is being represented in the same 3x3 area. I thought there might be some way that even affect both translation and rotation at the same time. (truly flipping the axis)
Edit: I'm pretty sure the operation you're looking for is changing coordinate systems while maintaining Z-up or Y-up. In this case, try setting all the elements of the second column (or row) of your matrix to their inverse.
This question would be better for the Math StackExchange. First, a really helpful read on rotation matrices.
The first problem is the matter of rotation order. I will be assuming the XYZ rotation order. We know the rotation matrices for each axis is as follows:
Given a matrix derived from the same rotation order, the resulting matrix would be as follows, where alpha is the X angle, beta is the Y angle, and gamma is the Z angle:
You can derive the individual components of each axis angle from this matrix. For example, you can derive the Y angle from -sin(beta) using some inverse trig. Given beta, you can derive alpha from cos(beta)sin(alpha). You can also derive gamma from cos(beta)sin(gamma). Note that the same number in the matrix can represent multiple values (e.g. sin(0)=0 and sin(180)=0).
Now that you know alpha, beta, and gamma, you can reverse beta and remake the rotation matrix.
There's a good chance that there's a better way to do this using quaternions, but you should ask the Math StackExchange these kinds of language-agnostic questions.
Much shorter answer: if you are not careful with your frame orientation many things down your pipeline are likely to have a bad hair day. The reason is "parity", a.k.a. "frame orientation", a.k.a. "right-handedness" (or rarely left-handedness). Most 3D geometry tools and libraries that work together normally assume implicitly that all coordinate systems in play are right-handed (or at least consistently-handed). Inverting the orientation of just one axis in a coordinate system changes its orientation from right to left handed or viceversa.
So, suggestion for things to check & try in your problem:
Check that the frame you get from your API is right-handed. You do so
by computing the determinant of the 3x3 rotation part of your 4x4 transform matrix: it must be +1 or very close to it.
If it is -1, then flip one if its axis, i.e. change the sign of one of the columns of the 3x3 rotation.
Note carefully: I said "columns" because I assume that you apply a transform Q to a point x by multiplying as Q * x, x being a 4x1 column vector with the last component equal to one. If you use row vectors left-multiplied by Q you need flip a row.
If that determinant is +1, you have a bug someplace else.

Quaternion reaching gimbal lock

In attempt to avoid angle lock when performing rotations I've tried to switch over to Quaternions. Somehow, I'm still managing to reach gimbal lock.
I'm not sure if its due to the math I've implemented, or a design error, so please point out if I should change my approach for my object coordinates.
Each of my objects hold an X,Y,Z value, and a pitch,yaw,roll value.
When I change a rotation value, the object recalculates its vertices based on the above information. This logic is as follows:
// vertex array
vertices[x] -= /*Offset by origin point*/;
// Quat.'s representing rotation around xyz axes
Quaternion q1 = Quaternion(glm::vec3(1,0,0),pitch);
Quaternion q2 = Quaternion(glm::vec3(0,1,0),yaw);
Quaternion q3 = Quaternion(glm::vec3(0,0,1),roll);
// total rotation
Quaternion TotalRot = ( (q3 * q2) * (q1) );
// conversion of original coordinates to quaternion
Quaternion Point1 = Quaternion(0, vertices[x].x(), vertices[x].y(), vertices[x].z());
// resulting rotated point
Quaternion Point2 = Quaternion( (TotalRot * Point1) * TotalRot.inverse() );
// placing new point back into vertices array
vertices[x] = QVector3D(round(Point2.v.x),round(Point2.v.y),round(Point2.v.z));
vertices[x]+= /*Undo origin point offset*/;
"vertices[]" is the objects vertex array. The origin point offset commented out above is just so that the object is rotated around the proper origin point, so it is shifted relative to 0,0,0 since rotations occur around that point (right?).
I have a pictorial representation of my problem, where I first yaw by 90, pitch by 45, then roll by -90, but the roll axis became parallel to the pitch axis:
Edit:
I tried multiplying those 3 axis quaternions together, then multiplied by a 4x4 matrix, followed by multiplying that by my vertex point, but I still gimbal lock/reach singularity!
Quaternion q1 = (1,0,0,pitch);
Quaternion q2 = (0,1,0,yaw);
Quaternion q3 = (0,0,1,roll);
Quaternion qtot = (q1*q2)*q3;
Quaternion p1(0, vertices[x].x(), vertices[x].y(), vertices[x].z());
QMatrix4x4 m;
m.rotate(qtot);
QVector4D v = m*p1;
vertices[x] = QVector3D(v.x(),v.y(),v.z());
Your issue is that even when you're using quaternions, you're still storing three pitch, yaw, and roll values, rather than a quaternion, to represent an object's orientation.
Here's how you should use quaternions for rotation here:
Instead of storing X, Y, Z, pitch, yaw, roll for each object, instead store X, Y, Z, orientation
in each object, where orientation is a quaternion starting at the initial value (0, 0, 0, 1), meaning no rotation. Storing pitch, yaw, and roll for each object is vulnerable to singularities (gimbal lock) because as small changes are added, one of the intermediate rotations (say, a pitch) could result in the object being parallel to a rotation axis (say, the yaw axis), so that the next rotation about that axis could fail.
Then, as an object is rotated, determine the pitch, yaw, and roll for that object that occurred during that frame (assuming that your input device provides the rotation in that form), convert it to a quaternion, then pre-multiply that quaternion into the object's orientation quaternion. This approach is less vulnerable to singularities because the changes in the rotation are expected to be very small each frame.
Don't modify the object's X, Y, and Z (your verticies array) directly after changing the orientation. Instead, when an object's orientation changes, create a new rotation matrix to serve as part of the object's world transformation matrix (along with scaling and translation; for best results, calculate the world transform as translation * rotation * scaling).
Every few frames, you should normalize the orientation quaternion to avoid undesirable changes in the orientation, which can happen due to rounding error.
If you take a Euler angles representation and turn it into quaternions just for doing the vector rotations, then you are still just using Euler angles. The gimbal lock problem will remain as long as you have Euler angles involved anywhere. You need to switch completely to quaternions and never go through a Euler angles representation anywhere at all if you want to completely eliminate that problem.
The basic way to do it is that you can use Euler angles at the far ends of your calculations (as the original input or as the ultimate output) if that is more convenient for you (e.g., Euler angles are often a more "human-readable" representation). But, use quaternions for everything else (and you can convert occasionally to rotation matrices, because they are more efficient for rotating vectors), and never do intermediary conversions to any of the "bad" rotation representations (Euler angles, axis-angles, etc.). The "gimbal-lock-free" benefits of quaternions and rotation matrices only apply if you stick with those two representations throughout all your calculations.
So, on one side, you have the singular representations (the formal term for "gimbal lock" is singularity):
Euler angles (of any kind, of which there are 12, by the way); and
axis-angle.
And on the other side, you have the singularity-free representations:
quaternions (more efficient memory-wise and for composition operations); and
rotation matrices (more efficient for applying rotations to vectors).
Whenever you operate (like putting several rotations together, or moving a rotating object) with a singular representation, you will have to worry about that singularity in every calculation you do. When you do those same operations with a singularity-free representation, then you don't have to worry about that (but you do have to worry about the constraints, which is the unit-norm constraint for quaternions and the proper orthogonality for rotation matrices). And of course, any time you convert to or from a singular representation, you have to worry about the singularity. This is why you should only do the conversions at the far ends of your calculations (when entering or leaving), and stick with the non-singular representations all through your calculations otherwise.
The only good purpose for Euler angles is for human readability, period.

Directx11 rotation around center of world?

I've been playing around for awhile now with directx and can't figure out how to rotate something without rotating it all the way around (0,0,0). The farther I get away from the center of my world the bigger circles in makes during its rotation.
Don't forget the matrix transformations apply to the coordinate system. Let's say you want to move your object upwards on an xy plane by 20 units, and rotate it by 90 degrees. If you rotate by 90 degrees first, you'll be rotating the entire plane by 90 degrees. This means 90 degrees is the new "upwards" when you translate up the y axis.
So, we translate first, so that our object's center is 0,0. Now when we rotate, we should be rotating around the center of the object. Of course, don't forgot to translate back, or clear the matrix somehow.
The order does matter when doing matrix transformations, as I'm sure you know. Usually, you should translate, scale, then rotate.
If you need a rotation α, around a point p located at (x₀,y₀,z₀), you create the matrix :
T(-x₀,-y₀,-z₀) * R(α) * T(x₀,y₀,z₀)
T means Translation and R means rotation. Also, depending on your convention such as row or column matrix, you may have to revert the order of operation.

How to constrain 3D rotations (Euler)

What's the correct/best way of constraining a 3D rotation (using Euler angles and/or quaternions)?
It seems like there's something wrong with my way of doing it. I'm applying the rotations to bones in a skeletal hierarchy for animation, and the bones sometimes visibly "jump" into the wrong orientation, and the individual Euler components are wrapping around to the opposite end of their ranges.
I'm using Euler angles to represent the current orientation, converting to quaternions to do rotations, and clamping each Euler angle axis independently. Here's C++ pseudo-code showing basically what I'm doing:
Euler min = ...;
Euler max = ...;
Quat rotation = ...;
Euler eCurrent = ...;
// do rotation
Quat qCurrent = eCurrent.toQuat();
qCurrent = qCurrent * rotation;
eCurrent = qCurrent.toEuler();
// constrain
for (unsigned int i = 0; i < 3; i++)
eCurrent[i] = clamp(eCurrent[i], min[i], max[i]);
One problem with Euler angles is that there are multiple ways to represent the same rotation, so you can easily create a sequence of rotations that are smooth, but the angles representing that rotation may jump around. If the angles jump in and out of the constrained range, then you will see effects like you are describing.
Imagine that only the X rotation was involved, and you had constrained the X rotation to be between 0 and 180 degrees. Also imagine that your function that converts the quaternion to Euler angles gave angles from -180 to 180 degrees.
You then have this sequence of rotations:
True rotation After conversion After constraint
179 179 179
180 180 180
181 -179 0
You can see that even though the rotation is changing smoothly, the result will suddenly jump from one side to the other because the conversion function forces the result to be represented in a certain range.
When you are converting the quaternion to Euler angles, find the angles that are closest to the previous result. For example:
eCurrent = closestAngles(qCurrent.toEuler(),eCurrent);
eConstrained = clampAngles(eCurrent,min,max);
remember the eCurrent values for next time, and apply the eConstrained rotations to your skeleton.
The problem here is that the constraints you are applying have no relation to the rotation be applied. From a conceptual point of view this is what you are trying to achieve:
assume a bone is in an unconstrained state.
apply rotation
has bone exceeded constraints? If yes, rotate it to where it is not constrained any more
Your code that clamps the Euler rotations is the part where you rotating the bone back. However this code ignores the original bone rotation so you will see odd behavior, such as the snapping you are seeing.
A simple way to work with this is to do this instead:
assume a bone is in an unconstrained state
apply rotation
test if bone exceeded constraints
if yes, we need to find where the constraint stops movement.
halve the rotation, apply it in reverse
is the bone exceeding constraints? If yes go to 1
If no, halve the rotation, apply it in the forward direction. Goto 2
keep doing that until you are within some tolerance of your constraining angles
Now this will work, but because your rotation quarternions is being applied on all angles, the rotation will stop when any one of those constraints are net, even if there is freedom some where else.
If instead you apply rotations independently of each other, then you will be able to reliable use your clamping or the above technique to honor constraints, and also rotate as closely to your target as you can.
-
-

How to get maya like rotations?

I am trying to achieve the same rotational effect like Maya in my project.
I have some knowledge on quaternions and the trackball example.
Unfortunately I am still unable to wrap my head around the concept of using the quaternions to get the desired effect.
Basically I am still getting the same issue I had before with the 3d trackball. After flipping the object upside down, and then trying to rotate to the right, the object will rotate to the left. Well actually its my camera rotating around the focus point in the opposite direction.
The problem is that I am using the screen coordinates & trackball to get the old / new vectors and getting the angle of rotation from those two vectors. I will always get the wrong axis of rotation this way.
How should I go about solving this issue?
I don't know Maya so I can only guess that its rotation is like this: if you rotate left-right, it feels natural. Then if you rotate the object up-down 180 degrees, then rotate left-right again, it still feels natural.
If you are familiar with the concept of using a matrix to do transformations (like rotate, scale and translate), well a quaternion is just the same concept but it only allows rotations, so you might want to use it to constrain your transforms to just rotations. In practice, you can use either a matrix or a quaternion to do the same thing.
What you need to do is remember the current quaternion state for the object, then when the next frame of rotation occurs, multiply the new rotation with the old quaternion (in that order) to give you the next frame's quaternion. That will ensure that no matter what orientation the object is in, the next frame's rotation will be applied from the viewer's viewpoint. This is as opposed to some naive rotation where you just say "user is scrolling up/down, therefore alter the object's X-axis rotation", which causes that flipping.
Remember, like matrices, quaternions need to be multiplied in reverse order that the actions are actually applied, which is why I said to multiply the new operation by the existing quaternion.
To finish with an example. Let's say the user is going to perform 2 actions:
On frame 1, the user rotates the object 180 degrees about the X axis (up/down rotation).
On frame 2, the user rotates the object 90 degrees about the Y axis (left/right rotation).
Lets say the object has a quaternion Q. Every frame, you will reset the object to its default coordinates and apply the quaternion Q to rotate it. Now you might initialise it with the identity quaternion, but let's just say the initial quaternion is called Q0.
On frame 1, create a new quaternion R1 which is a "rotate 180 degrees about the X axis" quaternion (you can find some maths to compute such a quaternion). Pre-multiply the new operation by the existing quaternion: Q1 = R1 * Q0.
On frame 2, create a new quaternion R2 which is a "rotate 90 degrees about the Y axis" quaternion. Pre-multiply the new operation by the existing quaternion: Q2 = R2 * Q1.
On frame 1 you will use Q1 to display the object, and on frame 2 you will use Q2. You can simply keep applying any subsequent user actions to the quaternion and it will always be rotated in the viewer's frame of reference.
I think you have problems with changing coordinate system.
Suppose, you want to rotate object in X Axis, then in Y Axis, and then move it and scale. So, you should multiply your transformation maxtrix (at the beginning it equals to itentity matrix) to the rotation matrix (firstly to X, then to Y), then to translation matrix and at the end to scaling matrix. So, when your current matrix multiplies to the resulting matrix, your coordinate systems changes.
To avoid this problem you can use 2 methods:
1) to accumulate your resultig matrix as product of all previous matrices.
2) to use stack, where in the top will be the matrix, which equals to product of all matrices in the bottom of this matrix (in the stack).
P.S. I'm not sure, that it helps you. I never used quaternions in my projects.