Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

Good day all.

I have a 3d object with a random rotation. I can express that object's rotation both through a quaternion or through an euler rotation.

Then I have a vector3 normal that I get from a raycast.

What I need, is to apply a rotation that will make a chosen axis of the 3d object, coincide with the normal vector3 from above.

The closest I got to the solution is this (trying to align the 'up' vector):

Quaternion q;
Vector3 a = Vector3.Cross(3dobject.up, varnormal);
q.x = a.x;
q.y = a.y;
q.z = a.z;
q.w = Mathf.Sqrt((3dobject.up.sqrMagnitude) * (varnormal.sqrMagnitude))
       + Vector3.Dot(3dobject.up, varnormal);

3dobject.rotation = q * 3dobject.rotation;

But when the normal is negative on the y (e.g. 0,-1,0), the object aligns with its y pointing up instead.

What's wrong with the formula? Is there a better solution maybe?

Thanks in advance.

share|improve this question
add comment

2 Answers 2

up vote 3 down vote accepted

Your formula is correct. I would suggest normalising the quaternion at the end, though, or any stage involving interpolation will likely be messed up.

What I believe is happening is that your up vector is (0, something, 0) and your normal vector is (0, -1, 0). In this very specific corner case (when the input vectors are opposite to each other) there is no unique solution to your problem so you will need to choose an arbitrary rotation around any vector that is orthogonal to up.

If you have a left vector in your 3dobject, you could do the following:

Quaternion q;
Vector3 a = Vector3.Cross(3dobject.up, varnormal);
float d = Vector3.Dot(3dobject.up, varnormal);
if (d < 0.0 && a.sqrMagnitude == 0.0) /* Replace with a real epsilon test */
{
    q.x = 3dobject.left.x;
    q.y = 3dobject.left.y;
    q.z = 3dobject.left.z;
    q.w = 0.0;
}
else
{
    q.x = a.x;
    q.y = a.y;
    q.z = a.z;
    q.w = Mathf.Sqrt((3dobject.up.sqrMagnitude) * (varnormal.sqrMagnitude)) + d;
}

3dobject.rotation = q * 3dobject.rotation;
share|improve this answer
add comment

To illustrate, look at this image generated by Wolfram Alpha:

You have your 3 local coordinate axes and you want to coincide any of them with a given forth vector (for instance (1/2, 1/2, 1/2) as in the image). To do that, you need to rotate your object. A rotation is best described as a combination of an angle and axis to rotate around. So how do we find out those two components?

Let's assume that you need to coincide the x axis with a normal vector named n.

The angle to rotate with can be calculated from either the cross or the dot product, but it is easier with the latter. Since a dot product of vectors u and v is |u||v|cos(u,v) we can calculate the angle between our vectors using this simple formula (in pseudocode):

float angle = Math.Acos(Vector3.Dot(x, n) / (x.GetLength() * n.GetLength()));

The axis to rotate around is as simple as the angle. Basically, the 2 vectors of ours and the origin of the object define a plane. A plane defines a 2d coordinate system and all we need is to actually rotate in that system. Rotation in 2d is done by rotating around a vector perpendicular to the plane and a vector perpendicular to the plane is the cross product of the vectors defining the plane:

Vector3 axis = Vector3.Normalize(Vector.Cross(x, n));

Now that we have both components, use your favourite math library to create the need rotation:

Quaternion rotation = Quaternion.CreateFromAxisAngle(axis, angle);
share|improve this answer
1  
You are basically making four Sqrt calls, one Acos, one Cos and one Sin (hidden in CreateFromAxisAngle) in order to get exactly the result that was in the original question. I would not recommend doing this. –  Sam Hocevar Sep 3 '13 at 13:05
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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