- Home /
Problem finding relative rotation from one quaternion to another
I want to find a relative rotation between two quaternions quatA and quatB, i.e. quatC is a quaternion rotation that if applied to quatA would result in quatB. Basically I want something like this:
quatC = Quaternion.RelativeRotation( quatA, quatB );
The only way I can think of to do it is to make two Transforms modA and modB, with modB the child of modA, and use the following code:
modA.rotation = quatA;
modB.rotation = quatB;
quatC = modB.localRotation;
However I would prefer not to use Transforms, and just use quaternions instead.
This previous answer suggests the following code would work:
Quaternion rotationDelta = Quaternion.FromToRotation(modelA.transform.forward, modelB.transform.forward);
But it doesn't appear to work after all, as it only inputs two direction vectors, not rotations. To demonstrate this, attach the following script to a basic cube dragged into an empty scene. The code will position and size the cube.
Modify rotations for models A and/or B. Model C should display the RELATIVE rotation FROM A TO B. In other words, applying C's rotation to A's rotation should result in B's rotation.
Model D shows the ACTUAL result of applying C's rotation to A's rotation. If C showed the actual relative rotation, D would be identical to B. As you will see, this is NOT the case. Hopefully I'm just missing something, but if not, is there a way to get quatC only with code?
var modA : Transform; var modB : Transform; var modC : Transform; var modD : Transform; var D_Rot_Equals_B_Rot = true;
// Nothing in Start is important to the question, it just sets up // other objects to help demonstrate the problem function Start() { modA = transform; modA.gameObject.name = "Model A"; modA.position = Vector3(0,0,0); modA.rotation = Quaternion.identity; modA.localScale = Vector3(.1,.1,.8); modB = GameObject.CreatePrimitive(PrimitiveType.Cube).transform; modB.gameObject.name = "Model B"; modB.position = Vector3(1,0,0); modB.localScale = Vector3(.1,.1,.8); modC = GameObject.CreatePrimitive(PrimitiveType.Cube).transform; modC.gameObject.name = "Model C"; modC.position = Vector3(2,0,0); modC.localScale = Vector3(.1,.1,.8); modD = GameObject.CreatePrimitive(PrimitiveType.Cube).transform; modD.gameObject.name = "Model D"; modD.position = Vector3(3,0,0); modD.localScale = Vector3(.1,.1,.8); var bump : Transform; bump = GameObject.CreatePrimitive(PrimitiveType.Sphere).transform; bump.gameObject.name = "Angle Helper"; bump.localScale = Vector3(.2,.2,.2); bump.position = Vector3(0,.15,-.3); bump.parent = modA; bump = GameObject.CreatePrimitive(PrimitiveType.Sphere).transform; bump.gameObject.name = "Angle Helper"; bump.localScale = Vector3(.2,.2,.2); bump.position = Vector3(1,.15,-.3); bump.parent = modB; bump = GameObject.CreatePrimitive(PrimitiveType.Sphere).transform; bump.gameObject.name = "Angle Helper"; bump.localScale = Vector3(.2,.2,.2); bump.position = Vector3(2,.15,-.3); bump.parent = modC; bump = GameObject.CreatePrimitive(PrimitiveType.Sphere).transform; bump.gameObject.name = "Angle Helper"; bump.localScale = Vector3(.2,.2,.2); bump.position = Vector3(3,.15,-.3); bump.parent = modD; }
function Update () {
// Modify rotations for models A and/or B.
// Model C should display the RELATIVE rotation FROM A TO B.
// In other words, applying C's rotation to A's rotation should result in B's rotation.
// Model D shows the ACTUAL result of applying C's rotation to A's rotation.
// If C showed the actual relative rotation, D would be identical to B.
// As you will see, this is NOT the case.
modC.rotation = Quaternion.FromToRotation(modA.forward, modB.forward);
modD.rotation = modA.rotation * modC.rotation;
if (modD.rotation==modB.rotation) {
D_Rot_Equals_B_Rot = true;
} else {
D_Rot_Equals_B_Rot = false;
}
}
Answer by Mike 3 · Nov 26, 2010 at 12:57 PM
This should do it:
Quaternion relative = Quaternion.Inverse(a) * b;
Nearly! I tested it both ways and it's actually the other way round - "Quaternion relative = Quaternion.Inverse(a) * b;" works a charm. Thanks! I'll tick this answer, maybe you could update it?
Oh strange, I tested that like 5 times, I must have had a thinko when I wrote it in here. Fixing it now
Thanks! It helped me too. I used to have headache from some problem with planets, and had been suffered with that for weeks, and with this solution anything got to work. Thanks again.:)
Weird, I wonder why in Unity the from is inverted and comes first, while in regular mathematics (at least according the the following link), the from is still inverted but comes last. http://stackoverflow.com/questions/1755631/difference-between-two-quaternions
I have the same doubt that @$$anonymous$$ax-Pixel, can someone explain why in Unity we put the Inverse first? Is this really correct or the Inverse in last can work as well?
Answer by HonoraryBob · Jul 06, 2018 at 09:53 PM
I tried to use the suggested code, but for some reason the result is always (0,0,0,1) even when there is a difference between the two quaternions as proven by using debug.log. Here's the code I'm using:
var ThisRot:Quaternion = transform.rotation;
var RotChange:Quaternion = Quaternion.Inverse(LastRot) * ThisRot;
Debug.Log("LastRot ="+LastRot+" ThisRot="+ThisRot+" RotChange="+RotChange);
LastRot = ThisRot;
Anyone know why this doesn't work?
[Edit: it turns out that it only seemed it didn't work because when Debug.Log prints out quaternions there's a one-digit limit for the fractional value, which means small angles don't show up.]
Answer by LarryTheBrave · Apr 27, 2019 at 05:24 PM
I would convert the rotations to Euler Angles, do a Vector3 difference, then convert back to Quaternions. This can be done with .eulerAngles against the transform.rotation and then Quaternion.Euler outside the parentheses enclosing the Vector3 subtraction. To simply get the angle you can do Quaternion.Angle.
Surprisingly, this is wrong. It makes sense, but it's completely not-even-close wrong. If you have two eulerAngle rotations, the inverse is not Quaternion.Euler(difference-in-xyz's). Put another way q1*(difference in xyz's) != q2. Not the other way around, either. You can test by computing the inverse and printing the euler angles.
It's because in q1*q2, q2 is on the local axis of the q1. If you went 20 on the real y, you can't cancel it by going -20 on the local y.
Answer by n-gist · Feb 13 at 12:35 AM
"quaternion rotation that if applied to quatA would result in quatB" is:
quatB * Quaternion.Inverse(quatA)
Here is how you can check it:
var quatA = Quaternion.Euler(10, 10, 10);
var quatB = Quaternion.Euler(15, 20, 25);
var relative1 = quatB * Quaternion.Inverse(quatA);
var relative2 = Quaternion.Inverse(quatA) * quatB;
var check1 = relative1 * quatA;
var check2 = relative2 * quatA;
Debug.Log(relative1.eulerAngles);
Debug.Log(relative2.eulerAngles);
Debug.Log("check relative1 " + check1.eulerAngles);
Debug.Log("check relative2 " + check2.eulerAngles);
Output:
(7.75, 7.65, 13.43) // Just to show that
(6.75, 8.66, 13.34) // order is important
check relative1 (15.00, 20.00, 25.00) // Correct one, equal to quatB
check relative2 (14.04, 20.95, 24.69)