04 February 2017

Math – Using quaternions to create a rotation matrix

The best way I found to create a rotation matrix with all three axes is with quaternions. The following code is C#:

Initialize

The Quaternion class has four properties: W, X, Y and Z. "W" is the angle, and the other represent the axis. I will only use unit quaternions, meaning the rotation is around one of the three axes only. You initialize an identity quaternion by setting them to (1, 0, 0, 0). I create one for each rotation like so:

var rotx = new Quaternion(Rotation.X, 1, 0, 0);
var roty = new Quaternion(Rotation.Y, 0, 1, 0);
var rotz = new Quaternion(Rotation.Z, 0, 0, 1);

The quaternion's constructor initializes those arguments as:

double r = (angle * (Math.PI / 180));
W = (float)Math.Cos(r / 2);

double s = Math.Sin(r / 2);
X = (float)(x * s);
Y = (float)(y * s);
Z = (float)(z * s);

Multiply

Next, we combine them all into one quaternion:

var rot = rotx * roty * rotz;

The method that multiplies two quaternions is defined as follows:

public static Quaternion operator *(Quaternion a, Quaternion b)
{
    var c = new Quaternion();
    c.W = a.W * b.W - a.X * b.X - a.Y * b.Y - a.Z * b.Z;
    c.X = a.W * b.X + a.X * b.W + a.Y * b.Z - a.Z * b.Y;
    c.Y = a.W * b.Y - a.X * b.Z + a.Y * b.W + a.Z * b.X;
    c.Z = a.W * b.Z + a.X * b.Y - a.Y * b.X + a.Z * b.W;
    return c;
}

Rotate

To get the matrix I needed to pass to OpenGL, I created another class called "Matrix", which is a 4x4 matrix containing a 16 element array. Then a method takes the quaternion and adds the rotation to this matrix. It took a while to find a way to do this that worked with OpenGL, but I finally found it in the .net framework.

var matrix = new Matrix();
matrix.Rotate(rot); //"rot" being the quaternion from before.

public void Rotate(Quaternion rotation)
{
    float x2 = rotation.X + rotation.X;
    float y2 = rotation.Y + rotation.Y;
    float z2 = rotation.Z + rotation.Z;

    float xx = rotation.X * x2;
    float xy = rotation.X * y2;
    float xz = rotation.X * z2;
    float yy = rotation.Y * y2;
    float yz = rotation.Y * z2;
    float zz = rotation.Z * z2;
    float wx = rotation.W * x2;
    float wy = rotation.W * y2;
    float wz = rotation.W * z2;

    Values[0, 0] = 1.0f - (yy + zz);
    Values[0, 1] = xy + wz;
    Values[0, 2] = xz - wy;

    Values[1, 0] = xy - wz;
    Values[1, 1] = 1.0f - (xx + zz);
    Values[1, 2] = yz + wx;

    Values[2, 0] = xz + wy;
    Values[2, 1] = yz - wx;
    Values[2, 2] = 1.0f - (xx + yy);
}

See also

Align a rotation matrix with a normal

No comments:

Post a Comment