Search code examples
c#unity-game-enginequaternions

In Unity, why does a roll of 360 degree represented in Quaternion have a different value?


Quaternion in Unity has been quite reliable despite some quirks in terminology (e.g. being in a reference frame that is both left-handed and right-handed), and for many months I've taken it for granted. Today I decided to increase my test coverage to ensure that its behaviour won't change in the following update, so I write the following test:

        [Test]
        public void RollRoundtrip()
        {
            // Arrange
            var u1 = Quaternion.identity;
            var u2 = Quaternion.Euler(0, 0, 360);
            var list = new[] { u1, u2, u2.normalized }.ToList();

            // Assert
            Assert.AreEqual(1, list.Distinct().Count(), string.Join(", ", list));
        }

In Quaternion.Euler(0,0,360), the roll angle is applied first, and should reset the rotation to its original representation. To my surprise, this test failed, with the 3 value being:

(0.00000, 0.00000, 0.00000, 1.00000), (0.00000, 0.00000, 0.00000, -1.00000), (0.00000, 0.00000, 0.00000, -1.00000)

This is obviously wrong, in fact, (0,0,0,-1) is not even normal. What may have caused this error? To me, the Unity Quaternion is extremely battle-tested, speculating a bug causing this would be very unlikely.


Solution

  • Well, everything you discovered is expected.

    Quaternions essentially can be seen as a single rotation axis (the vector part, (x,y,z) components) as well as a 4th component that is simply the cosine of half the angle you rotate around that given vector. A quaternion of (0,0,0,1) or (0,0,0,-1) are both identity rotations. You don't have any axis to rotate around.

    A quaternion always rotates around a single axis in 3d space. So just forget about the concept of euler angles which are 3 consecutive rotations carried out one after the other in a particular order. A quaternion directly rotates from one orientation to another and that's always possible through a single axis rotation. That's why quaternions are so great when it comes to interpolation. There are no gimbal dependencies.

    To construct a quaternion manually from an angle (a) and a given unit rotation axis (v) you would literally do

    q.x = v.x * sin(a/2);
    q.y = v.y * sin(a/2);
    q.z = v.z * sin(a/2);
    q.w = cos(a/2);
    

    When you rotate by 360°, it means the sine of 180° is just 0. So there's no rotation axis to rotate around and you end up at the same orientation anyways. The cosine of is 1 while the cosine of 180° is -1. Both represent the exact same orientation and both do not have a rotation axis.

    I'm not sure what you mean by being a left and right handed coordinate system. Unity uses a left handed system everywhere (with the possible exception of lowlevel matrices in shaders).

    The quaternion value (0,0,0,-1) is a normalized value. It's magnitude is exactly 1.0. So I'm not sure why you think this should be wrong. I can't see anything wrong here.

    As it was mentioned in the comments, just like euler angles have multiple ways to represent the same orientation, so do quaternions. In fact simply inverting all components of a quaternion represents the same orientation. The vector part will be flipped. So it's still the same vector, but it's flipped 180°. Since w is also flipped, the rotation will rotate in the other direction. Since the rotation direction and the rotation axis are flipped, you would still rotate the same way.