Search code examples
c++vectorrotationquaternions

Quaternion Rotation Between Two Sets of Vectors


I would like some help on finding the rotation between two sets of two vectors in 3D as a quaternion. In each pair, the vectors would be at right angles to each other and have unit length (basically two vectors in an orthonormal basis).

My current code looks something like this:

Vector3 look1, up1, right1, look2, up2, right2;
// look1, up1, right1 form orthonormal basis,
// ditto with look2, up2, and right2
Vector3 lookRotateAxis = look1 % look2;
// operator% defined as cross product for convenience
float lookRotateAngle = acos(look1 * look2);
// operator* defined as dot product for convenience
Quaternion lookRotate;
lookRotate.makeRotation(lookRotateAngle, lookRotateAxis);
up1Transformed = lookRotate.rotateVector(up1);
Vector3 upRotateAxis = up1Transformed % up2;
float upRotateAngle = up1Transformed * up2;
Quaternion upRotate;
upRotate.makeRotation(upRotateAngle, upRotateAxis);
Quaternion finalRotation = upRotate * lookRotate;
// quaternion finalRotation rotates basis 1 to basis 2

The method I use right now is to find the quaternion rotation between the two vectors first of each pair, and then multiply by the quaternion rotation between the two vectors second of each pair. This results in a single quaternion that I can rotate both vectors by to get the other two vectors. Is there a simpler way to compute the quaternion, not by multiplying the two rotations, but computing a single rotation?

Thank you!


Solution

  • I think it's possible to do what you want very quickly and efficiently. First thing, you should complete each orthonormal pair of vectors into an orthonormal basis. The obvious way to do so is by taking the cross product of the first two vectors. Order matters: if you want u0 to map to v0 and u1 to map to v1, then form the orthonormal basis {u0,u1,u2} where u2 = u0 x u1 (cross product), also form the orthonormal basis {v0,v1,v2} where v2 = v0 x v1, and map u2 to v2. If you weren't careful and set v2 = v1 x v0, you'd end up with an impossible situation (trying to map a right-handed coordinate system to a left-handed coordinate system with a rotation). So be careful about the order of terms in cross products.

    Now that you have two orthonormal bases, or frames, it's easy to construct orthogonal matrices representing rotations from the {x,y,z} frame to the given frame. (Again, you have to give some thought to the orientation or handedness of the {x,y,z} frame ... you may have to use {x,z,y} instead, for example.) The matrix multiplication by an orthogonal matrix representing a rotation from the frame {x,y,z} to {u0,u1,u2} is

    [u00 u01 u02]
    [u10 u11 u12]
    [u20 u21 u22]
    

    where, according to the usual convention for computer graphics, we pre-multiply a vector by our matrix. So for example, the effect of our matrix on (1,0,0) is

            [u00 u01 u02]
    [1 0 0] [u10 u11 u12] = [u00 u01 u02]
            [u20 u21 u22]
    

    which is just what we want; the same for the other two basis vectors in {x,y,z}.

    To map one frame to another, we go through the frame {x,y,z} as an intermediary. So we have to find the inverse of the first orthogonal matrix. Fortunately inverting an orthogonal matrix is very easy: you just take the transpose. So to map the frame {u0,u1,u2} to the frame {v0,v1,v2}, use the matrix product

    [u00 u10 u20] [v00 v01 v02]
    [u01 u11 u21] [v10 v11 v12]
    [u02 u12 u22] [v20 v21 v22]
    

    Let's see what happens when we input the vector u1 = [u10,u11,u12] into this matrix product:

                  [u00 u10 u20] [v00 v01 v02]           [v00 v01 v02]
    [u10 u11 u12] [u01 u11 u21] [v10 v11 v12] = [0 1 0] [v10 v11 v12] = [v10 v11 v12]
                  [u02 u12 u22] [v20 v21 v22]           [v20 v21 v22]
    

    just as required. Here we have used the formulas u1 . u0 = 0, u1 . u1 = 1, u1 . u2 = 0 which follow from {u0,u1,u2} being an orthonormal frame.

    So the orthogonal matrix representing the rotation you want is exactly

    [u00 u10 u20] [v00 v01 v02]
    [u01 u11 u21] [v10 v11 v12]
    [u02 u12 u22] [v20 v21 v22]
    

    Perform the matrix multiplication to get a single matrix, then if you want the quaternion representation, convert from rotation matrix to quaternion by something like the method described in http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion.