7

I have two objects, and each object has two vectors:

  • normal vector
  • up vector

Like on this image:

enter image description here

Up vector is perpendicular to normal vector. Now I want to find unique rotation from one object to another, how to do that?

I have one method to find rotation between one vector to another, and it works. The problem is that I need to take care the two vectors: normal vector and up vector. If I use this method to rotate normal vector from object one to normal from object two, the up vector could be pointing wrong way, and they needs to be parallel.

Here is the code for finding the shortest rotation:

GE::Quat GE::Quat::fromTo(const Vector3 &v1, const Vector3 &v2)
{
    Vector3 a = Vector3::cross(v1, v2);
    Quat q;

    float dot = Vector3::dot(v1, v2);

    if ( dot >= 1 ) 
    {
        q = Quat(0,0,0,1);

    }
    else if ( dot < -0.999999 )
    {
        Vector3 axis = Vector3::cross(Vector3(1,0,0),v2);

        if (axis.length() == 0) // pick another if colinear
                axis = Vector3::cross(Vector3(0,1,0),v2);
        axis.normalize();
        q = Quat::axisToQuat(axis,180);
    }
    else
    {
        float s = sqrt( (1+dot)*2 );
        float invs = 1 / s;

        Vector3 c = Vector3::cross(v1, v2);

        q.x = c.x * invs;
        q.y = c.y * invs;
        q.z = c.z * invs;
        q.w = s * 0.5f;
    }
    q.normalize();
    return q;
}

What should I change/add to this code, to find the correct rotation?

Tom
  • 1,027
  • 2
  • 16
  • 35
  • 1
    Sorry if this is a stupid question, but what is the difference between "shortest rotation" and "unique rotation"? – Proxy Feb 24 '14 at 19:23
  • Maybe I did not make myself clear, sorry about that. I just want to rotate normal vector to other normal vector but the up vector must be correct too. If I use this method posted, it rotates only one vector therefore the up vector could be pointing the wrong direction, but I need it to be same direction. Sorry for my english – Tom Feb 24 '14 at 19:44
  • @Proxy I edited my question, maybe now it is more clear – Tom Feb 24 '14 at 20:02
  • 1
    Do you want the resulting rotation to (a) rotate one object so it is parallel to the other OR (b) rotate the first object position to the second object position around some origin? – TonyWilk Feb 24 '14 at 20:35
  • I just want a rotation that normal vector of object one will be parallel to normal vector of object two, and vector up of object one will be parallel to vector up of object two :) so option (a) – Tom Feb 24 '14 at 20:42

3 Answers3

8

Before we begin, I will assume that both UP vector and normal vector are normalized and orthogonal (dot product is zero) between them.

Let's say that you want to rotate your yellow plate to be aligned with the rose (red?) plate. So, our reference will be the vectors from yellow plate and we will call our coordinate system as XYZ, where Z -> normal yellow vector, Y -> Up yellow vector and X -> YxZ (cross product).

In the same way, for rose plate, the rotated coordinate system will be called X'Y'Z' where Z' -> normal rose vector, Y' -> up rose vector and X' -> Y'xZ' (cross product).

Ok to find the rotation matrix, we only need to make sure that our normal yellow vector will become normal rose vector; that our up yellow vector will be transfomed in the up rose vector, and so on, i.e.:

RyellowTOrose = |X'x   Y'x   Z'x|
                |X'y   Y'y   Z'y|
                |X'z   Y'z   Z'z|

in other words, after you have any primitives transformed to be in coordinates of yellow system, applying this transformation, will rotate it to be aligned with rose coordinates system

If your up and normal vector aren't orthogonal, you can correct one of them easily. Just make the cross product between normal and up (results in a vector called C, for convenience) and do again the cross product between with C and normal, to correct the up vector.

Amadeus
  • 10,199
  • 3
  • 25
  • 31
  • So simple... Thanks for Your answer, I feel like an idiot now :) – Tom Feb 25 '14 at 14:32
  • One more little question - instead of cross(Y,Z) should not be cross(Z,Y)? – Tom Mar 01 '14 at 20:17
  • 1
    @Tom This is following the right hand rule, where the coordinate system is XYZ, or X = YxZ, Z = XxY and Y = ZxX. If you are following another convention, just change it – Amadeus Mar 03 '14 at 14:25
  • Very simple and clear answer @Amadeus, The below statement is bit confusing "If your up and normal vector aren't orthogonal, you can correct one of them easily. Just make the cross product between normal and up (results in a vector called C, for convenience) and do again the cross product between with C and normal, to correct the up vector.". Can u help to understand better ? – Whoami Feb 18 '23 at 08:31
3

First of all, I make the claim that there is only one such transformation that will align the orientation of the two objects. So we needn't worry about finding the shortest one.

Let the object that will be rotated be called a, and call the object that stay stationary b. Let x and y be the normal and up vectors respectively for a, and similarly let u and v be these vectors for b. I will assume x, y, u, and v are unit length, and that is x is orthogonal to y, and u is orthogonal to v. If any of this is not the case code can be written to correct this (via planar projection and normalization).

Now let’s construct matrices defining the “world space” the orientation of a and b. (let ^ denote the cross product) construct z as x ^ y, and construct c as a ^ b. Writing x, y, z and a, b, c to columns of each matrix gives us the two matrices, call them A and B respectively. (the cross product here gives us a unit length and mutually orthogonal vector since the same is true of the operands)

The change of coordinate system transformation to obtain B in terms of A is A^-1 (the inverse of matrix A, where ^ denotes a generalization of an exponent), in this case A^-1 can be computed as A^T, the transpose, since A is an orthogonal matrix by construction. Then the physical transformation to B is just matrix B itself. So, transforming an object by A^-1, and then by B will give the desired result. However these transformations can be concatenated into one transformation by multiplying B on the right into A^-1 on the left.

You end up with this matrix (assuming no arithmetic errors):

 _                                                                                                                                                                                             _
| x0*u0+x1*u1+x2*u2                                    x0*v0+x1*v1+x2*v2                                    x0*(u1*v2-u2*v1)+x1*(u2*v0-u0*v2)+x2*(u0*v1-u1*v0)                                  |
|                                                                                                                                                                                               |
| y0*u0+y1*u1+y2*u2                                    y0*v0+y1*v1+y2*v2                                    y0*(u1*v2-u2*v1)+y1*(u2*v0-u0*v2)+y2*(u0*v1-u1*v0)                                  |
|                                                                                                                                                                                               |
| (x0*y2-x2*y1)*u0+(x2*y0-x0*y2)*u1+(x0*y1-x1*y0)*u2   (x0*y2-x2*y1)*v0+(x2*y0-x0*y2)*v1+(x0*y1-x1*y0)*v2   (x0*y2-x2*y1)*(u1*v2-u2*v1)+(x2*y0-x0*y2)*(u2*v0-u0*v2)+(x0*y1-x1*y0)*(u0*v1-u1*v0) |
|_                                                                                                                                                                                             _|
Apriori
  • 2,308
  • 15
  • 16
2

The quaternion code rotates just one vector to another without "Up" vector.

In your case simply build rotation matrix from 3 orthogonal vectors

  1. normalized (unit) direction vector
  2. normalized (unit) up vector
  3. cross product of direction and up vectors.

Than you will have R1 and R2 matrix (3x3) representing rotation of object in two cases.

To find rotation from R1 to R2 just do

 R1_to_R2 = R2 * R1.inversed()

And matrix R1_to_R2 is the transformation matrix from one orientation to other. NOTE: R1.inversed() here can be replaced with R1.transposed()

minorlogic
  • 1,872
  • 1
  • 17
  • 24