• Create Account

Banner advertising on our site currently available from just \$5!

Direction Vector to Rotation Matrix

Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

16 replies to this topic

#1Yezu666  Members   -  Reputation: 100

Like
0Likes
Like

Posted 20 May 2011 - 04:54 AM

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

#2haegarr  Crossbones+   -  Reputation: 5234

Like
1Likes
Like

Posted 20 May 2011 - 05:02 AM

...
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 )-1 = R-1 * T( p )-1 = Rt * 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.)

#3Yezu666  Members   -  Reputation: 100

Like
0Likes
Like

Posted 20 May 2011 - 06:17 AM

( T( p ) * R )-1 = R-1 * T( p )-1 = Rt * T( -p )

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

#4Yezu666  Members   -  Reputation: 100

Like
0Likes
Like

Posted 20 May 2011 - 08:10 AM

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);
}

return ret;
}


Edited by Yezu666, 20 May 2011 - 09:27 AM.

"In the arena of logic, I fight unarmed."
Red Mage, Episode 835: Refining Moment

#5gsamour  Members   -  Reputation: 140

Like
0Likes
Like

Posted 20 May 2011 - 08:29 AM

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)

#6haegarr  Crossbones+   -  Reputation: 5234

Like
1Likes
Like

Posted 20 May 2011 - 09:00 AM

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.

#7gsamour  Members   -  Reputation: 140

Like
0Likes
Like

Posted 20 May 2011 - 09:04 AM

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)

#8haegarr  Crossbones+   -  Reputation: 5234

Like
0Likes
Like

Posted 20 May 2011 - 09:18 AM

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 ex and ey, i.e. the unit vectors in x and y direction.
ex x ey = [ 1 0 0 ]t x [ 0 1 0 ]t = [ 0 0 1 ]t
what give you the unit vector in z direction. No dependency of a handedness, because [ 0 0 1 ]t is correct in both systems!

If you would instead compute it in reverse order
ey x ex = [ 0 1 0 ]t x [ 1 0 0 ]t = [ 0 0 -1 ]t
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 )t = vt * Mt
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.

#9Yezu666  Members   -  Reputation: 100

Like
0Likes
Like

Posted 20 May 2011 - 09:39 AM

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 )

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

#10Yezu666  Members   -  Reputation: 100

Like
0Likes
Like

Posted 20 May 2011 - 09:50 AM

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

#11gsamour  Members   -  Reputation: 140

Like
0Likes
Like

Posted 20 May 2011 - 09:54 AM

I definitely need to brush up on this stuff to have it clear in my head. Thanks haegarr, hopefully we've helped the OP

#12gsamour  Members   -  Reputation: 140

Like
0Likes
Like

Posted 20 May 2011 - 09:55 AM

@Yezu666,

#13haegarr  Crossbones+   -  Reputation: 5234

Like
0Likes
Like

Posted 20 May 2011 - 10:15 AM

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:

Please bear in mind that some aspects of the implementation are not clear yet. So I must guess some things...

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

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

I assume that Vector3d.sub(a, b) computes a-b (in this order).

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

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

Normalizing "plane_up" is not strictly necessary, because both arguments "side" and "forward" have unit length and "side" is guaranteed to be orthogonal to "forward".

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;

Now things become a bit complicated.

1. You're negating (solely) the forward direction, so your camera is defined to look along its local negative z direction, right?

2. Because you're about to compute the transposed matrix, from the indexing I conclude that you're using the column major layout, right? I mean that the matrix indices are arranged like so:
[ 0 4 8 12 ]
[ 1 5 9 13 ]
[ 2 6 10 14]
[ 3 7 11 15]

If that is right it would further mean that you are using column vectors.

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

TransformationMatrix ret = new TransformationMatrix(matrix);
ret.multiply(TransformationMatrix.calculateTranslationMatrix(neweye));

The ret.multiply computes probably Rt * T( -eye ) here. This order would be correct if and only if you're using column vectors. Do you do so? Unfortunately, nothing in the shown code gives a clear criterion whether you do so.

    return ret;
}

Nothing to say here

#14scgames  Members   -  Reputation: 2002

Like
0Likes
Like

Posted 20 May 2011 - 01:58 PM

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

Actually, (spatial) handedness has no effect on how the cross product is computed or how the arguments should be ordered.

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)

Although these terms are commonly conflated, they refer to two completely different and unrelated things (as haegarr explained above).

#15gsamour  Members   -  Reputation: 140

Like
0Likes
Like

Posted 20 May 2011 - 03:19 PM

Thanks jyk and haegarr for clearing this up

EDIT: can you go into a little more detail please?

From Wolfram Mathworld... http://mathworld.wolfram.com/CrossProduct.html

It says that the way the cross-product is computed there assumes the right-hand rule. Now I'm confused.

#16scgames  Members   -  Reputation: 2002

Like
0Likes
Like

Posted 20 May 2011 - 03:36 PM

EDIT: can you go into a little more detail please?

From Wolfram Mathworld... http://mathworld.wol...ossProduct.html

It says that the way the cross-product is computed there assumes the right-hand rule. Now I'm confused.

I'm not sure why that specification is made (perhaps it's because the example in the diagram is with respect to a right-handed system).

In any case, the cross product is computed in the same way regardless of handedness; the 'right-hand rule' and 'left-hand rule' are just mnemonics to remember in which direction the cross product will point in a coordinate system with a given handedness.

#17WiredCat  Members   -  Reputation: 482

Like
0Likes
Like

Posted 20 May 2011 - 04:05 PM

yeh i need it! direction of cross product lololol

how can you be sure that direction is correct lol

Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

PARTNERS
{"extraction_info": {"found_math": true, "script_math_tex": 0, "script_math_asciimath": 0, "math_annotations": 0, "math_alttext": 0, "mathml": 0, "mathjax_tag": 0, "mathjax_inline_tex": 0, "mathjax_display_tex": 0, "mathjax_asciimath": 1, "img_math": 0, "codecogs_latex": 0, "wp_latex": 0, "mimetex.cgi": 0, "/images/math/codecogs": 0, "mathtex.cgi": 0, "katex": 0, "math-container": 0, "wp-katex-eq": 0, "align": 0, "equation": 0, "x-ck12": 0, "texerror": 0, "math_score": 0.35779261589050293, "perplexity": 2270.3150883491667}, "config": {"markdown_headings": false, "markdown_code": true, "boilerplate_config": {"ratio_threshold": 0.18, "absolute_threshold": 10, "end_threshold": 15, "enable": true}, "remove_buttons": true, "remove_image_figures": true, "remove_link_clusters": true, "table_config": {"min_rows": 2, "min_cols": 3, "format": "plain"}, "remove_chinese": true, "remove_edit_buttons": true, "extract_latex": true}, "warc_path": "s3://commoncrawl/crawl-data/CC-MAIN-2015-14/segments/1427131300031.56/warc/CC-MAIN-20150323172140-00288-ip-10-168-14-71.ec2.internal.warc.gz"}