🔒 Locked
Rotating A Normal Using Another Normal
Advertisement
Original Post
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]
[Edited by - EmptyVoid on July 29, 2007 11:03:23 AM]
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:
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:
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:
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]
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]
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.
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.
Quote: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).
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.
Quote: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).
Set the real component of the quaternion to be the dot product of the normals plus one.
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:
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.
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 );}Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement