1
\$\begingroup\$

I have a direction vector on which I have to apply some rotation to align it to positive z-axis. To use Matrix.CreateRotationX(angle) of XNA, I need the angle for which I'd have to compute cos or tan inverse.

I think this is a complex task to do. Also, eventually those are also converted to sin(angle) and cos(angle) in the matrix.

Is there any inbuilt way to create rotation matrix from a 3D vector?

However, I can write the function but still asking if there is one already there.

\$\endgroup\$
  • 1
    \$\begingroup\$ What would the point of reference for the angle be? \$\endgroup\$ – Liam McInroy Jun 7 '14 at 18:02
  • \$\begingroup\$ It would be the origin. \$\endgroup\$ – Shashwat Jun 7 '14 at 18:25
  • 1
    \$\begingroup\$ @Shashwat the origin isn't a direction... \$\endgroup\$ – Tiago Costa Jun 7 '14 at 22:54
  • \$\begingroup\$ I don't understand what you are trying to achieve... Do you want to find which rotations you have to apply to a direction vector so it is parallel with the positive z-axis? \$\endgroup\$ – Tiago Costa Jun 7 '14 at 22:56
  • \$\begingroup\$ @TiagoCosta! Yes, but without using trigonometric functions. \$\endgroup\$ – Shashwat Jun 8 '14 at 12:44
2
\$\begingroup\$

You are very much correct in your thinking that you shouldn't be using trigonometric functions where you don't absolutely need them. Particularly in cases where your calculation is using both a trigonometric method and its inverse (noting, as you do, that Matrix.CreateRotation* uses sin and cos internally).


However, you need more than a single vector to accomplish what you are trying to do!

What Outlaw Lemur and Tiago Costa were pointing out in comments is that you don't get a rotation just by specifying a single vector. You need to specify that vector relative to another vector. Traditionally people just choose a fixed vector representing "up" - in XNA: Vector3.Up.

So if you passed Vector3.Up to your rotation method, you would expect to get no rotation at all.

But, even then, this is not enough to fully specify a rotation. If you picture the above system in your head, imagine you have an "up" axis that you are rotating into a new position. But you're still free to rotate around that new "up" axis. You need another vector to select a specific rotation around that axis.

For this, people typically choose "forward" or "right" or "left" (and Vector3 has these static properties as well, for XNA's right-handed coordinate system).

So with an "up" and a "forward" vector, you have fully defined the rotation of an object.


Now, the original question was: does XNA have a built-in method for creating such a rotation matrix? Yes it does. It also lets you simultaneously specify a translation. It's:

Matrix.CreateWorld(Vector3 position,
                   Vector3 forward,
                   Vector3 up);

(MSDN)

(And there's also CreateLookAt, which does a similar thing for cameras.)

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Let the vector be (x,y,z), then that relative vector (as I mentioned in the question) is (0,0,z). So, now I have A = sin(angleX) = x/sqrt(x*x+z*z) and B = sin(angleY) = y/sqrt(y*y+z*z). My question is to use A and B to create the rotation matrix. I guess Matrix.CreateWorld with position as origin will solve my problem. \$\endgroup\$ – Shashwat Jun 8 '14 at 13:01
2
\$\begingroup\$

The following code conveys the intent of RandyGaul's answer translated into Xna(ease).

Matrix RotationNeededToAlignArbitraryVectorWithZAxis(Vector3 arbitraryVector)
{
    arbitraryVector.Normalize();
    Vector3 axis = Vector3.Cross(arbitraryVector, Vector3.UnitZ);
    float partialAngle = (float)Math.Asin(axis.Length());
    float finalAngle = arbitraryVector.Z < 0f ? MathHelper.PiOver2 - partialAngle + MathHelper.PiOver2 : partialAngle;
    axis.Normalize();

    return Matrix.CreateFromAxisAngle(axis, finalAngle);
}

It so happens that when you need to cross a couple normalized vectors (usually to find the axis), the magnitude of the result is the arcSin of the angle that separates the 2 vectors just as the dot product is the arccosine of the angle that separates them. So since a cross is necessary in this case, I used the cross result to derive the angular value instead of calculating the dot product.

The order of the arguments in the cross is important, don't swap them.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ To clarify, swapping the cross product arguments would require you to flip the signs in your conditional operator, since the resulting perpendicular vector would be flipped. \$\endgroup\$ – RandyGaul Jun 8 '14 at 1:46
  • \$\begingroup\$ This needs Asin which I want to avoid. \$\endgroup\$ – Shashwat Jun 8 '14 at 13:03
1
\$\begingroup\$

You can probably use the axis angle representation to build a rotation matrix, with whatever math library you have available.

If you dot your vector (normalized) with the z axis vector (normalized) and take the inverse cosine of this result, you have the angle you need to rotate by. To get the vector to apply this rotation along you can cross your vector with the Z axis to get an appropriate perpendicular.

From here it's just a matter of using the axis angle representation of a rotation matrix.

Vector v;
float theta = acos( dot( v, zAxis ) );
Matrix3 m = AxisAngle( theta, normalize( cross( v, zAxis ) );

Please note that this code will give poor results when your vector is nearly parallel with the z axis. To avoid this problem it would be sufficient to make sure the length of the cross product result is clearly not near zero before executing the rest of the code.

As Steve pointed out in the comments this isn't quite enough as you'll need to know which direction to rotate about your vector, either theta or -theta. I haven't worked out these details, so I'd recommend taking a look at his comment.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Randy, your solution is challenged when v is pointing more than 90 degrees away from zAxis (dot results are acute only). It might be worth adding a test to see if v.z is less than zero. If so, then angle = 1/2pi - theta + 1/2pi. \$\endgroup\$ – Steve H Jun 8 '14 at 0:37
  • \$\begingroup\$ @RandyGaul! Taking the inverse cosine is something I want to avoid, because that angle will eventually be converted to sines and cosines. So, its a waste of computation. \$\endgroup\$ – Shashwat Jun 8 '14 at 12:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.