Skip to main content
gamedev.net
🔒 Locked

Rotating A Normal Using Another Normal

Started by EmptyVoid 11 replies 1.3k views
Original Post
EmptyVoid
EmptyVoid
I basically need to rotate one normal using another how do I do this?
This is your life, and it's ending one minute at a time. - Fight club
Vorpy
Vorpy
You basically need to state your problem more clearly.
EmptyVoid
EmptyVoid
OK actually I just need to rotate a 3D vector around a its origin witch is 0,0,0 based on a 3D normal vector starting at 0,0,1 sorry not sure how to explain better.

[Edited by - EmptyVoid on July 28, 2007 9:23:55 PM]
This is your life, and it's ending one minute at a time. - Fight club
erissian
erissian
Do you want to:

A) Rotate a normal so that it is aligned with a given normal

B) Rotate a normal along an axis defined by a given normal

C) Rotate a normal along a given axis so that it is aligned with a given normal
We''re sorry, but you don''t have the clearance to read this post. Please exit your browser at this time. (Code 23)
EmptyVoid
EmptyVoid
I think C maybe not sure.
This is your life, and it's ending one minute at a time. - Fight club
chand81
chand81
You probably just need to convert axis angle to a rotation matrix and use it to transform your 3d vector.
http://en.wikipedia.org/wiki/Rotation_matrix
EmptyVoid
EmptyVoid
Could but would like to actually know the math behind it and it would be better just to do it with a math formula because it will be used inside a vertex shader. also I think it's the same formula a vertex shader would use for billboarding.

[Edited by - EmptyVoid on July 29, 2007 11:03:23 AM]
This is your life, and it's ending one minute at a time. - Fight club
erissian
erissian
Ok, so here's a better question. Instead of trying to figure out what solution you're trying to work out, what problem are you trying to solve? For what reason are you trying to "rotate one normal using another"?
We''re sorry, but you don''t have the clearance to read this post. Please exit your browser at this time. (Code 23)
erissian
erissian
OK, thanks to your PM I've determined that you need solution A, rotating an object with a normal Ni in such a manner that its normal becomes Nf. I'm assuming that you do your rotations in XYZ order. Also, I'm assuming that X is right, Y is up, and Z is through the screen.

Since the first step is to rotate on X, we need to find out what angle to rotate it by. Rotating on the X axis won't affect the X component of your normal, but it will change the Y and Z portions. Specifically, one will shrink while the other grows, and then reverse. This happens in such a way that the overall length does not change - the length will remain 1.

The easiest way to do this is to find the respective angle from the Z axis for both vectors; in other words if one is n degrees from Z and the other is m degrees, then you should rotate by m-n degrees. We'll solve this with atan2f(y,z). This will look like:

// Rotate from a to bvoid rotate_to_normal(Vector a, Vector b) {  float Rx_a = atan2f(a.y, a.z);  float Rx_b = atan2f(b.y, b.z);  glRotatef( (Rx_b - Rx_a)*180.0f/M_PI, 1, 0, 0 );  ...


Now we have to do the same process, except with the Y axis. However, we can't use the same two vectors - remember that we just rotated our object, so it's normal is pointing somewhere else already. We have to figure out what that is. We'll left-multiply it by Rx, a rotation matrix for the X axis, which looks like this:
Rotation on X:     | 1  0  0 |Rx = | 0  C -S |  (C = cosθ<sub>x</sub>)     | 0  S  C |  (S = sinθ<sub>x</sub>)Our normal:    | Nx |N = | Ny |    | Nz |Rotation matrix left-multiplied (because of column-major matrices)         | 1  0  0 || Nx |   | Nx          |Rx * N = | 0  C -S || Ny | = | Ny*C + Nz*S |         | 0  S  C || Nz |   | Nz*C - Ny*S |


So then we take this new normal, and do what we did before, except for the Y axis. This will use atan2f(x,z). Here's what is what that looks like:

  ...  float c = cosf( Rx_b - Rx_a );  float s = sinf( Rx_b - Rx_a );  Vector a_r; // a rotated (on X)  a_r.x = a.x;  a_r.y = a.y *  c + a.z * s;  a_r.z = a.y * -s + a.z * c;    Rx_a = atan2f(a_r.x, a_r.z);  Rx_b = atan2f(b.x, b.z);  glRotatef( (Rx_b - Rx_a)*180.0f/M_PI, 0, 1, 0 );}


Now, there's definitely a more streamlined way to go about this, but this should work for now.

Edit: Fixed some extra semicolons.

[Edited by - erissian on August 2, 2007 2:37:00 AM]
We''re sorry, but you don''t have the clearance to read this post. Please exit your browser at this time. (Code 23)
Vorpy
Vorpy
There's a really clever way to compute the rotation you want as a quaternion. I have no idea who came up with this, but apparently it's been in the Game Programming Gems books.

Set the real component of the quaternion to be the dot product of the normals plus one. Set the imaginary components to the components of the cross product of the normals. Normalize the quaternion. The quaternion now represents the rotation you want, and can easily be used to directly rotate points or to build a rotation matrix. Information about using quaternions in this way is much easier to find than information about this very concise way to compute a rotation from one normal to another.

This algorithm doesn't work if the normals are at a 180 degree angle from each other. Just check for this special case by seeing if the dot product of the normals is close to -1. If you try to use the algorithm in this situation, the quaternion goes to (0,0,0,0) and cannot be normalized. Instead, just pick a 180 degree rotation about any angle.
Zakwayda
Zakwayda
Quote:
Original post by Vorpy
There's a really clever way to compute the rotation you want as a quaternion. I have no idea who came up with this, but apparently it's been in the Game Programming Gems books.
If you're talking about the 'Shortest Arc Quaternion' article from GPG I, that's actually a different algorithm. As for the method you have in mind, I don't know who came up with it either, but I picked it up from someone with the screen name 'minorlogic' a while back (either on Flipcode, or maybe on Martin Baker's math site).
Quote:
Set the real component of the quaternion to be the dot product of the normals plus one.
For the benefit of the OP, I'll mention that the algorithm can also be used with vectors of arbitrary (non-zero) length; just replace the '1' with the product of the vectors' lengths (which is of course 1 when the vectors are unit length).
erissian
erissian
So the last function I posted was a little wasteful, with all the trigonometric functions using values we got from the arctangent and so forth. Can we streamline this?

Well, let's look at one of the last steps; we calculate the sine and cosine for that angle. This is something we can do right up front. This is from the identity:
a ˙ b = |a||b|cosΘ

So we can get the cosine just from the dot product, and since we're dealing with normals, we know |a| and |b| are both going to be 1.

That, and since cosΘ2 + sinΘ2 = 1, we can find the sine with:
sine = sqrtf(1.0f - cosine);

So, we have our sines and cosines for the initial rotation. But don't we have to find the degrees so we can rotate? Not really; we can make the matrix ourselves:

void rotate_x (Vector a, Vector b) {  float Cx = a.dot(b);  float Sx = 1.0f - Cx*Cx;  float rotation_matrix[16] = {  1.0f, 0.0f, 0.0f, 0.0f,                                 0.0f,   Cx,  -Sx, 0.0f,                                 0.0f,   Sx,   Cx, 0.0f,                                 0.0f, 0.0f, 0.0f, 1.0f };  glMultMatrix( rotation_matrix );}


We can use this same method for two degrees of rotation, we just have to figure out what our new vector is. We worked that out in the last example, so we'll just create a new vector with the same results. Then, we'll create a matrix that is the result of Ry*Rx.

With this example, we do pretty much was glRotatef does already, saving the necessity for doing it again. We also trade 6 trig functions for two sqrts.

void rotate_to_normal(Vector a, Vector b) {  float Cx = a.dot(b);  float Sx = sqrtf(1.0f - Cx*Cx);  Vector c = Vector( a.x, a.y*Cx + a.z*Sx, -a.y*Sx + a.z*Cx );  float Cy = c.dot(b);  float Sy = sqrtf(1.0f - Cy*Cy);  float rotation_matrix[16] = {  Cy   , 0.0f, -Sy   , 0.0f,                                -Sx*Sy,   Cx,  Sx*Cy, 0.0f,                                 Cx*Sy,  -Sx,  Cx*Cy, 0.0f,                                  0.0f, 0.0f,   0.0f, 1.0f };  glMultMatrix( rotation_matrix );}
We''re sorry, but you don''t have the clearance to read this post. Please exit your browser at this time. (Code 23)
EmptyVoid
EmptyVoid
OK Thanks

[Edited by - EmptyVoid on August 4, 2007 6:21:05 PM]
This is your life, and it's ending one minute at a time. - Fight club

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.