Search Unity

Understanding rotations in local and world space - Quaternions?

Discussion in 'Editor & General Support' started by Zediron, Oct 2, 2012.

  1. Zediron

    Zediron

    Joined:
    Sep 27, 2012
    Posts:
    5
    Hello everyone,

    I have been playing around with Unity for a while now. I've been checking out existing project for inspiration on how to do thing. Now I would like to re-implement some of the things I saw, just to make sure I understand all that :)

    1.) When I apply Transform.Rotate() it seems to rotate around whatever Axis I want it to relative to the previous rotation's value.
    The code sample in the docs is as follows:
    Code (csharp):
    1.         transform.Rotate(Vector3.right * Time.deltaTime);
    2.         transform.Rotate(Vector3.up * Time.deltaTime, Space.World);
    If I wanted to express that same thing with Quaternions, how would you do it?

    The second rotation seems simple:
    Code (csharp):
    1. transform.rotation = Quaternion.Lerp ( transform.rotation, Quaternion.Euler(Vector3.up), Time.deltaTime);
    But how do you apply the first rotation around the local Vector3.right axis with Quaternions?

    Would the
    Code (csharp):
    1.  
    2. transform.localRotation = Quaternion.Lerp(transform.localRotation, Quaternion.Euler(Vector3.right), Time.deltaTime);
    3. transform.rotation = Quaternion.Lerp ( transform.rotation, Quaternion.Euler(Vector3.up), Time.deltaTime);
    4.  
    be equal to the rotation from the first code snippet?

    2.) Nice thing about Quaternion.Lerp is that I can let the object to rotate the object by absolute values over time or approximate it. If I did not use Quaternions but I used Rotate() method, how could I maximize the rotation of the object? eg: I want to rotate that object by 30 degrees around the world's Y axis in 3 seconds in equal steps: 10degrees/second and then apply a 30degree rotation around to local X axis, again with 10 degrees/second.
    Is it easier to do with Quaternions?

    3.) Combining Quaternions is confusing the hell out of me.
    Example:
    Code (csharp):
    1. Quaternion rotR = Quaternion.Euler(Vector.right * Time.deltaTime);
    2. Vector3 localUp = transform.InverseTransformDirection(Vector3.up);
    3. Quaternion rotU = Quaternion.Euler(localUp * Time.deltaTime);
    4. transform.rotation = rotR*rotU;
    5.  
    6. // is the above equal with the following if I replace the transform.rotation line with this:
    7. transform.rotation = rotR;
    8. transform.rotation = rotU;
     
  2. Chris-Herold

    Chris-Herold

    Joined:
    Nov 14, 2011
    Posts:
    116
    There is just one really simple rule you need to memorize: Order matters.

    Rotate around a local axis: rotation = rotation * Quaternion.AngleAxis(10, Vector3.Up);
    Rotate around a world axis: rotation = Quaternion.AngleAxis(10, Vector3.Up) * rotation;

    So, as you can see above, putting the desired rotation last rotates around a local axis, putting it first rotates around a world axis. There's not much more to know about combining Quaternions.
    You also don't need to know the local axis nor transform any desired rotation axis.
    Simply chose the right combine order and you're golden.

    Code (csharp):
    1.  
    2. Quaternion rotR = Quaternion.AngleAxis(10 * Time.deltaTime, Vector3.right);
    3. Quaternion rotU = Quaternion.AngleAxis(10 * Time.deltaTime, Vector3.up);
    4.  
    5. // rotate around World Right
    6. transform.rotation = rotR * transform.rotation;
    7. // rotate around Local Up
    8. transform.rotation = transform.rotation * rotU;
    9.  
    These are relative rotations.
    If you want to set an absolute rotation, use Quaternion.Identity as base rotation:

    Code (csharp):
    1.  
    2. Quaternion rotR = Quaternion.AngleAxis(45, Vector3.right);
    3.  
    4. // set rotation to 45 degrees around local right
    5. transform.rotation = Quaternion.Identity * rotR;
    6.  
    7.  
     
    Last edited: Oct 2, 2012
    Threeyes, LudiKha, Celezt and 18 others like this.
  3. HarvesteR

    HarvesteR

    Joined:
    May 22, 2009
    Posts:
    531
    An important thing to keep in mind to understand quaternions is, Don't think about the * operator as a multiplication. It's not. For Quaternions, the * operator is best thought of like a "rotates" operation.

    The way I think about it is this: The quaternion left of the * operator is rotating the quaternion (or vector) on the right.

    So this:

    rotA * rotB;

    Can be read as:

    rotA "rotates" rotB;


    With this in mind, Chris's examples above become very clear. If you rotate a rotation of 10 degrees about the X axis by the object's own rotation, that X axis will be rotated so that it becomes aligned to the the object's own X axis. The resulting rotation is the object's own, plus 10 degs around it's X axis. That's why this operation is dependent on order. Rotating the object's own rotation by 10 degrees around X means rotating it about the absolute X axis.

    Another useful thing to keep in mind is, the argument on the right of the "rotates" operator doesn't need to be another Quaternion. You can use Quaternions to rotate vectors too, like this:

    Code (csharp):
    1.  
    2.  
    3. // this rotates an absolute Z+ vector, so that it points to the character transform's own Z+ direction.
    4. Vector3 CharacterForward = character.rotation * Vector3.forward;
    5.  
    6.  
    As you'd expect then, the reverse operation with vectors isn't valid, as a vector can't modify a quaternion.


    Hope this helps.

    Cheers
     
  4. grojguy

    grojguy

    Joined:
    Jul 13, 2009
    Posts:
    35
    Wow, that is an incredibly-helpful explanation. Thank you.

    However, I am struggling with a case where the object's origin/pivot is not at the center of the mesh.

    Usually I do:
    Code (csharp):
    1. transform.RotateAround( renderer.bounds.center, transform.TransformDirection(Vector3.up), angle);
    That is, rotate the transform around local 'up' axis (converted to world space) passing through origin/pivot in world coordinates, by angle degrees

    But I would like learn how this rotation can be accomplished with Quaternions. I have tried all manner of combinations of the "rotate around Local up" as shown above, but nothing works properly. Since the object's pivot/origin is not at it's mesh center, using Vector3.up for the axis parameter does not yield the correct result. The closest I can get is...
    Code (csharp):
    1. transform.rotation = transform.rotation * Quaternion.AngleAxis( angle, renderer.bounds.center );
    ...which rotates the object on it's true mesh origin/pivot, but around the wrong axis (not local 'up'), but some other indiscernible axis.

    Any suggestions?
     
  5. shdome

    shdome

    Joined:
    Sep 24, 2015
    Posts:
    1
    I am very sorry to necro this thread, but this is the best match for my problem.

    What I’m trying to do is very simple, but I can’t get it right no matter what I do. I have a unit vector with coordinates J = (0.58, 0.58, 0.58). This is a unit vector. Let’s say it represents where some object is pointing. I then wish to rotate it by Pi/2 in the plane defined by the vector and Y axis. That means, as far as I understand, rotating J around local Z axis, since in its ‘local’ coordinates vector J lies along X axis. Below is the picture of the described situation. I expect that after transformation the new coordinates to be J’= (-0.58, 0.58, -0.58). However, I can’t achieve it.

    I turn J into pure quaternion
    Code (CSharp):
    1. absRotation = new Quaternion(J.x, J.y, J.z, 0)
    . Then I construct rotation quaternion as
    Code (CSharp):
    1. localRotation = Quaternion.AngleAxis(90, new Vector3(0f, 0f, 1f))
    . Then, I’ve tried a few approaches.

    A) First, I’ve done transformations like this:
    Code (CSharp):
    1. Quaternion result = localRotation * absRotation;
    2. result = result * localRotationConjugate;
    The result was surprising. R1 = (0.58, -0.58, 0.58). The angle between original and result vector is 70.52878 degrees. It seems like it is rotated around global X axis for some reason.

    B) Next, I’ve decided to try inverse the axis of rotation, since it seemed to be rotating the wrong way. My local rotation was now
    Code (CSharp):
    1. LocalRotation = Quaternion.AngleAxis(90, new Vector3(0f, 0f, -1f));
    After the same transformations the result I’ve got was R2 = (-0.58, 0.58, 0.58). Now it was rotating around global Y axis. I have no idea how inversing direction of a vector can rotate it 90 degrees. The angle between original vector and the result was still 70.52878 degrees.

    C) Then I tried the advice in this topic. Unfortunately, there isn’t a word in this topic about multiplying by a conjugate. So I tried the only transformation this topic recommended.
    Code (CSharp):
    1. Quaternion result = absRotation * localRotation;
    This is supposed to be rotation in local coordinates. I’ve got this result = (0.89, 0, 0.45), so it lies on XZ plane. The angle between the original vector and this result is 39.23152.

    Last, when you apply the second transformation to it
    Code (CSharp):
    1. result = result * localRotationConjugate;
    you get the original vector.

    By now I am at a complete loss what am I doing wrong and each iteration brings me further from solution.
     
    Last edited: Sep 24, 2015
  6. UnityVeris

    UnityVeris

    Joined:
    Jan 16, 2017
    Posts:
    2
    There are two ways of storing 3D rotation values in Unity.

    Euler (x, y, z)
    Quaternion (x, y, z, w)

    It's important to understand that x,y,z Euler values are not the same as x,y,z Quaternion values.

    I believe the problem with your code above is that you assume that they are the same and you cast from Euler to Quaternion. I'd suggest looking up the "Quaternion calculator" on Google, it has a useful visualization component that helps to understand the difference between the two data structures.
     
  7. JustinBillson

    JustinBillson

    Joined:
    Jun 27, 2017
    Posts:
    2
    THANK YOU SO MUCH KIND SIR <3 THE ORDER MATTERS!!
     
    dyupa and ModLunar like this.