Fries of Doom
Fries of Doom

Reputation: 179

Quaternion vector roation

My quaternion math is a bit rusty, and I'm trying to figure out which of the following implementations is more correct...

Looking here you can see Microsoft's version of transforming a vector by a quaternion: https://referencesource.microsoft.com/#System.Numerics/System/Numerics/Vector3.cs,347

Here is the code, and it has clearly been optimized:

        public static Vector3 Transform(Vector3 value, Quaternion rotation)
        {
            float x2 = rotation.X + rotation.X;
            float y2 = rotation.Y + rotation.Y;
            float z2 = rotation.Z + rotation.Z;
 
            float wx2 = rotation.W * x2;
            float wy2 = rotation.W * y2;
            float wz2 = rotation.W * z2;
            float xx2 = rotation.X * x2;
            float xy2 = rotation.X * y2;
            float xz2 = rotation.X * z2;
            float yy2 = rotation.Y * y2;
            float yz2 = rotation.Y * z2;
            float zz2 = rotation.Z * z2;
 
            return new Vector3(
                value.X * (1.0f - yy2 - zz2) + value.Y * (xy2 - wz2) + value.Z * (xz2 + wy2),
                value.X * (xy2 + wz2) + value.Y * (1.0f - xx2 - zz2) + value.Z * (yz2 - wx2),
                value.X * (xz2 - wy2) + value.Y * (yz2 + wx2) + value.Z * (1.0f - xx2 - yy2));
        }

However, this gives different results to my own (less optimized) implementation:

        public static Vector3 Transform(this Vector3 value, Quaternion rotation)
        {
            var q = new Quaternion(value.X, value.Y, value.Z, 0.0f);
            var res = rotation.Conjugate() * q * rotation;
            return new Vector3(res.X, res.Y, res.Z);
        }

        public static Quaternion operator *(Quaternion value1, Quaternion value2)
        {
            // 9 muls, 27 adds
            var tmp_00 = (value1.Z - value1.Y) * (value2.Y - value2.Z);
            var tmp_01 = (value1.W + value1.X) * (value2.W + value2.X);
            var tmp_02 = (value1.W - value1.X) * (value2.Y + value2.Z);
            var tmp_03 = (value1.Y + value1.Z) * (value2.W - value2.X);
            var tmp_04 = (value1.Z - value1.X) * (value2.X - value2.Y);
            var tmp_05 = (value1.Z + value1.X) * (value2.X + value2.Y);
            var tmp_06 = (value1.W + value1.Y) * (value2.W - value2.Z);
            var tmp_07 = (value1.W - value1.Y) * (value2.W + value2.Z);
            var tmp_08 = tmp_05 + tmp_06 + tmp_07;
            var tmp_09 = (tmp_04 + tmp_08) * 0.5f;

            return new Quaternion(
                tmp_01 + tmp_09 - tmp_08,
                tmp_02 + tmp_09 - tmp_07,
                tmp_03 + tmp_09 - tmp_06,
                tmp_00 + tmp_09 - tmp_05
                );
        }

Since these do not give the same results, one of them must be wrong, but which one is it and why?

My own implementation seems to work in the cases that I'm trying to use it in, and the MS implementation seems to be broken, but I would be surprised if it were actually incorrect, since I think it is very widely used...

Thanks! :)

Upvotes: 1

Views: 1275

Answers (1)

James Tursa
James Tursa

Reputation: 2636

There are two different conventions for rotating a vector via a quaternion, left chain and right chain. E.g.,

vnew = q * v * conjugate(q) <-- This is left chain (successive rotations stack up on the left)

vnew = conjugate(q) * v * q <-- This is right chain (successive rotations stack up on the right)

Left chain convention is typically used for active rotations, where the quaternion is being used to rotate a vector within a coordinate frame. I.e., v and vnew are two different vectors coordinatized within the same coordinate frame.

Right chain convention is typically used for passive rotations, e.g. quaternions that represent coordinate system transformations. I.e., v and vnew are actually the same vector, just coordinatized in two different coordinate frames.

The MS code you show above is a left chain convention, but your code is a right chain convention. Hence the different results.

Both conventions are valid and have their uses, but you need to be very careful when pulling code from online sources. You need to verify what the convention used by the code is in order to use it correctly. And you need to ensure the convention matches how you are using the quaternions in your particular application.

Upvotes: 1

Related Questions