1
votes

I am developing a controller in WPF 3D (using C#) to be able to easily move and rotate a ProjectionCamera using Move()- and Pitch()-functions. My controller will become a Behavior<ProjectionCamera> that can be attached to a ProjectionCamera. In order to initialize the controller, I want to calculate the current rotation of the camera by looking at its current Up and Forward-vectors and compare them to the default camera orientation (Up = [0 1 0], Forward = [0 0 -1]). In other words, I want to calculate a rotation that will transform the camera default's orientation to its current one.

Ultimately, I want to express the rotation as a single Quaternion, but as an intermediate step I first calculate the Proper Euler Angle rotations of the form z-N-Z expressed as AxisAngleRotation3D-values, following the default definition of Wikipedia:

var alphaRotation = CalculateRotation(z, x, N);
var betaRotation = CalculateRotation(N, z, Z);
var gammaRotation = CalculateRotation(Z, N, X);

with

CalculateRotation(Vector3D axisOfRotation, Vector3D from, Vector3D to) : AxisAngleRotation3D

The Euler Angle Rotations seem to be calculated correctly, based on some unit tests. However, when I convert these rotations to a single Quaternion, the resulting Quaternion represents a rotation that differs from the Euler Angle Rotations, and I don't know why.

This is how I convert the Euler Angles to a single Quaternion:

var rotation =
    new Quaternion(alphaRotation.Axis, alphaRotation.Angle) *
    new Quaternion(betaRotation.Axis, betaRotation.Angle) *
    new Quaternion(gammaRotation.Axis, gammaRotation.Angle);

For example, when I initialize a ProjectionCamera with an UpDirection of [1 0 0], meaning it's been rotated 90 degrees around its LookDirection axis ([0 0 -1]), the calculated Euler Angle Rotations are as follows:

alphaRotation -->  90 deg. around [0 1 0]
betaRotation  -->  90 deg. around [0 0 -1]
gammaRotation --> -90 deg. around [1 0 0]

My test verifies that, when applied in order, these rotations will transform the default Up-vector ([0 1 0]) into the current Up-vector ([1 0 0]), effectively rotating it 90 deg. around the [0 0 -1] axis. (It's also reasonable straightforward to verify this by hand.)

However, when I apply the calculated QuaternionRotation to the default Up-vector, it is transformed to the vector [-1 0 0], which is obviously wrong. I have hard-coded these results within a Unit Test and got the same results:

    [TestMethod]
    public void ConversionTest()
    {
        var vector = new Vector3D(0, 1, 0);

        var alphaRotation = new AxisAngleRotation3D(new Vector3D(0, 1, 0), 90);
        var betaRotation = new AxisAngleRotation3D(new Vector3D(0, 0, -1), 90);
        var gammaRotation = new AxisAngleRotation3D(new Vector3D(1, 0, 0), -90);

        var a = new Quaternion(alphaRotation.Axis, alphaRotation.Angle);
        var b = new Quaternion(betaRotation.Axis, betaRotation.Angle);
        var c = new Quaternion(gammaRotation.Axis, gammaRotation.Angle);
        var combinedRotation = a * b * c;

        var x = Apply(vector, alphaRotation, betaRotation, gammaRotation);
        var y = Apply(vector, combinedRotation);
    }

When you run the test above, you will see that x gives you the expected vector ([1 0 0]) but y will be different, where it should be exactly the same rotation.

What am I missing?

1
@Sinatr: Yes, I know the order of the rotations matter. Did you spot any mistake in my code? Because as far as I know, Euler Angles have to applied in the order (alpha, beta, gamma), and to represent this sequence of rotations as a single Quaternion, you have to multiply the rotations also in this order (alpha * beta * gamma).Wim.van.Gool

1 Answers

0
votes

I solved the issue. Apparantly, the order of the multiplication of the individual Quaternions should be reversed. So, to convert Euler Angles (or any set of rotations) into a single Quaternion in .NET you should do the following:

var rotation = gammaRotation * betaRotation * alphaRotation;

where rotation represents geometrically applying alphaRotation first, then betaRotation and finally gammaRotation. I just wished they had documented this, since the meaning of the order depends on the specific library you are working with....