Direction Vector to Rotation Matrix

Started by
15 comments, last by _WeirdCat_ 12 years, 11 months ago
Hello!
I've been trying to solve a (rather simple?) problem lately. I'm surprised I haven't been able to find anything really useful on the net so far.

The thing is I have 3d direction vector. Use it to set the direction of an object in 3d space. To do that I need of course a transformation matrix.
I have a function that properly calculates the LookAt matrix. However it is correct for the view transformation. I had no success in using it to calculate the rotation of the object.

I have also tried using trigonometry using (assuming Y is up):

if(dir.getY() != 0.0f)
xAngle = (float)Math.toDegrees(Math.atan((dir.getZ()*dir.getZ()+dir.getX()*dir.getX())/dir.getY()));

if(dir.getZ() != 0.0f)
yAngle = (float)Math.toDegrees(Math.atan(dir.getX()/dir.getZ()));

TransformationMatrix.calculateRotationMatrix(xAngle, yAngle, 0.0f);

However this does produce proper results only in certain situations.

Can anybody suggest a solution?

Thanks a lot :)
"In the arena of logic, I fight unarmed."
Red Mage, Episode 835: Refining Moment
Advertisement

...
The thing is I have 3d direction vector. Use it to set the direction of an object in 3d space. To do that I need of course a transformation matrix.
I have a function that properly calculates the LookAt matrix. However it is correct for the view transformation. I had no success in using it to calculate the rotation of the object.
...

The difference between the view transformation (a global-to-local transformation) and its camera transformation (a local-to-global transformation) is that the one is the inverse of the other. So, if your implementation actually computes a view transformation, then invert the resulting matrix and you're done.

Because the view transformation usually is composed of a translation and a rotation only, like so
T( p ) * R
for column vectors, its inverse is probably computed simply as
( T( p ) * R )[sup]-1[/sup] = R[sup]-1[/sup] * T( p )[sup]-1[/sup] = R[sup]t[/sup] * T( -p )
where the t denotes the transpose operation, so you may avoid a full-fledged matrix inversion implementation. (You have to reorder the matrices if you use row vectors, of course.)

( T( p ) * R )[sup]-1[/sup] = R[sup]-1[/sup] * T( p )[sup]-1[/sup] = R[sup]t[/sup] * T( -p )


Thanks for the answer :)
I'm trying to experiment with the transpose of the rotational part of the LookAt matrix, without much success unfortunately.
However the weird thing is that with the transposed matrix I get some scaling effects when applied to a mesh. Should that happen?
"In the arena of logic, I fight unarmed."
Red Mage, Episode 835: Refining Moment
Ok. Solved. Sort of :)

I've managed to implement a function using hybrid method. It seems to work. However is not very elegant.
For anyone interested:


public static TransformationMatrix calculateDirectionalMatrix(Vector3d dir, Vector3d up)
{
float [] matrix = new float[16];

//Calculating as if a normal LookAt function:
Vector3d forward = new Vector3d(dir);

//Only with inverted direction and no height component.
forward.setX(-forward.getX());
forward.setY(0.0f);

forward.normalize();

Vector3d side = Vector3d.cross(forward , up);
side.normalize();

Vector3d plane_up = Vector3d.cross(side , forward);
plane_up.normalize();

matrix[0] = side.getX();
matrix[1] = plane_up.getX();
matrix[2] = -forward.getX();
matrix[3] = 0.0f;

matrix[4] = side.getY();
matrix[5] = plane_up.getY();
matrix[6] = -forward.getY();
matrix[7] = 0.0f;

matrix[8] = side.getZ();
matrix[9] = plane_up.getZ();
matrix[10] = -forward.getZ();
matrix[11] = 0.0f;

matrix[12] = 0.0f;
matrix[13] = 0.0f;
matrix[14] = 0.0f;
matrix[15] = 1.0f;

//Complete directional matrix for the XZ part of the direction.
TransformationMatrix ret = new TransformationMatrix(matrix);

//Now if relevant we calculate the height component of the direction using the trigonometry function.
if(dir.getY() != 0.0f)
{
float angle = (float)Math.toDegrees(Math.asin(dir.getY()));
TransformationMatrix Y = TransformationMatrix.calculateRotationMatrix(angle, 0.0f, 0.0f);

//We multiply matrices as D = XZ * Y;
ret.multiply(Y);
}

//TADA!
return ret;
}
"In the arena of logic, I fight unarmed."
Red Mage, Episode 835: Refining Moment
If you have a direction vector (I'm assuming this means your "forward" vector), then all you need to do to create the transformation matrix is create a basis using that vector.

Pseudocode:

vector3 tempUp = [0, 1, 0]
vector3 right = forward.cross(tempUp)
vector3 up = right.cross(forward)

matrix3x3 rotationMat =

[ right.x forward.x up.x ]
[ right.y forward.y up.y ]
[ right.z forward.z up.z ]

(haven't double checked but i'm almost sure this is correct)


EDIT: this all depends on whether you're using a right-handed or left-handed system (column-major or row-major matrices)

I'm trying to experiment with the transpose of the rotational part of the LookAt matrix, without much success unfortunately.
However the weird thing is that with the transposed matrix I get some scaling effects when applied to a mesh. Should that happen?

Definitely not.


Ok. Solved. Sort of :)

I've managed to implement a function using hybrid method. It seems to work. However is not very elegant.
For anyone interested:


public static TransformationMatrix calculateDirectionalMatrix(Vector3d dir, Vector3d up)
{
float [] matrix = new float[16];

//Calculating as if a normal LookAt function:
Vector3d forward = new Vector3d(dir);

//Only with inverted direction and no height component.
forward.setX(-forward.getX());
forward.setY(0.0f); // <<< setY
forward.setY(-forward.getY()); // <<< setY

forward.normalize();

...


That code snippet is probably wrong: It negates the x component of the local forward vector variable, sets the y component to 0 and afterwards negates it, and does nothing with the z component. However, the method is ... abstruse. Why do you suppress the y component first and later consider it by rotation?


If you have a direction vector (I'm assuming this means your "forward" vector), then all you need to do to create the transformation matrix is create a basis using that vector.

Pseudocode:

vector3 tempUp = [0, 1, 0]
vector3 right = forward.cross(tempUp)
vector3 up = right.cross(forward)

matrix3x3 rotationMat =

[ right.x forward.x up.x ]
[ right.y forward.y up.y ]
[ right.z forward.z up.z ]

(haven't double checked but i'm almost sure this is correct)


EDIT: this all depends on whether you're using a right-handed or left-handed system (column-major or row-major matrices)

Well, not to be offending, but ...

1. A rotation matrix must be orthonormal. I.e. the length of each column / row vector must be 1. The cross product gives you a vector with the length
| a x b | = | a | * | b | * sin( <a,b> )
that is generally not 1 due to the sine, even if the participating vectors are of unit length. So some re-normalization is missed in your code!

E.g.

vector3 tempUp = [0, 1, 0]
forward.normalize();
vector3 right = forward.cross(tempUp)
right.normalize();
vector3 up = right.cross(forward)
up.normalize(); // (should not be necessary)

2. You've forgotten to mention that the matrix is for column vector use. "column-major" and "row-major" are terms that describe how the elements of a 2D matrix construct are arranged in linear (i.e. 1D) memory. Your code does no deal with this aspect in any way. Instead you meant "column vector use" vs. "row vector use".

3. How does "this all depends on whether you're using a right-handed or left-handed system" ?

4. Right-/left-handedness and matrix layout has absolutely nothing in common.
Not offended in any way :)


1. Oops... I forgot to normalize the vectors... Thanks!

2. The handedness affects the order in which you do the cross product

3. By row-major and column-major I meant the same thing as row-vectors and column-vectors (I've always heard of the term row-major and column-major referred to matrices)

Not offended in any way :)

Thanks :)


2. The handedness affects the order in which you do the cross product

Assuming you compute the cross-product of e[sub]x[/sub] and e[sub]y[/sub], i.e. the unit vectors in x and y direction.
e[sub]x[/sub] x e[sub]y[/sub] = [ 1 0 0 ][sup]t[/sup] x [ 0 1 0 ][sup]t[/sup] = [ 0 0 1 ][sup]t[/sup]
what give you the unit vector in z direction. No dependency of a handedness, because [ 0 0 1 ][sup]t[/sup] is correct in both systems!

If you would instead compute it in reverse order
e[sub]y[/sub] x e[sub]x[/sub] = [ 0 1 0 ][sup]t[/sup] x [ 1 0 0 ][sup]t[/sup] = [ 0 0 -1 ][sup]t[/sup]
you would get a wrong result!


3. By row-major and column-major I meant the same thing as row-vectors and column-vectors (I've always heard of the term row-major and column-major referred to matrices)

Using column or else row vectors is important for the matrix product. The correspondence between 2 matrices is the transpose:
( M * v )[sup]t[/sup] = v[sup]t[/sup] * M[sup]t[/sup]
where the left side uses a column vector v and the right side its row vector equivalent. Notice that the order is reverse, and notice that the vector / matrix is transposed compared to the original one.

On the other hand, row major layout means that a matrix
[ a b ]
[ c d ]
is stored number sequence
{ a b c d }
in memory, while the same matrix stored using column major layout looks like
{ a c b d }
This issue has nothing to do with matrix math but simply with the need for computer programs and/or file formats to serialize the 2D data of a matrix.

That code snippet is probably wrong: It negates the x component of the local forward vector variable, sets the y component to 0 and afterwards negates it, and does nothing with the z component. However, the method is ... abstruse. Why do you suppress the y component first and later consider it by rotation?

Well Indeed I made a typo. The part:
forward.setY(-forward.getY());is not needed. (Fixed it BTW :P)

I remove the Y component, because the upper method didn't provide proper rotation for it. So effectively I've split the operation into two rotations and then I multiply the resulting matrices. The method is not very straight forward but provides correct results. I'd gladly change it I if could find something simpler that calculates correctly.

Although I haven't noticed so far, I think there are errors in my LookAt function. AFAIK the resulting matrix should be orthonormal and the scaling effect after transposition suggests it is not. I must look into it. However I would be very happy if You could help me. :) Here is the code:


public static TransformationMatrix calculateLookAtMatrix(Vector3d eye, Vector3d center, Vector3d up)
{
float [] matrix = new float[16];

Vector3d forward = Vector3d.sub(center , eye);
forward.normalize();

Vector3d side = Vector3d.cross(forward , up);
side.normalize();

Vector3d plane_up = Vector3d.cross(side , forward);
plane_up.normalize();

matrix[0] = side.getX();
matrix[1] = plane_up.getX();
matrix[2] = -forward.getX();
matrix[3] = 0.0f;

matrix[4] = side.getY();
matrix[5] = plane_up.getY();
matrix[6] = -forward.getY();
matrix[7] = 0.0f;

matrix[8] = side.getZ();
matrix[9] = plane_up.getZ();
matrix[10] = -forward.getZ();
matrix[11] = 0.0f;

matrix[12] = 0.0f;
matrix[13] = 0.0f;
matrix[14] = 0.0f;
matrix[15] = 1.0f;

Vector3d neweye = new Vector3d(eye);
neweye.reverse();

TransformationMatrix ret = new TransformationMatrix(matrix);

ret.multiply(TransformationMatrix.calculateTranslationMatrix(neweye));

return ret;
}

thanks a lot for the help :)
"In the arena of logic, I fight unarmed."
Red Mage, Episode 835: Refining Moment

vector3 tempUp = [0, 1, 0]
vector3 right = forward.cross(tempUp)
vector3 up = right.cross(forward)

matrix3x3 rotationMat =

[ right.x forward.x up.x ]
[ right.y forward.y up.y ]
[ right.z forward.z up.z ]

Thanks for the suggestion. I've tried this method but I don't seem to get a correct transformation from it. I'll keep trying, though :)
"In the arena of logic, I fight unarmed."
Red Mage, Episode 835: Refining Moment

This topic is closed to new replies.

Advertisement