views:

1286

answers:

3

I'm drawing a flat disk using gluDisk() in my scene. gluDisk() draws the disk facing the positive Z axis but I want it to be facing some arbitrary normal I have.
Clearly I need to use glRotate() to get the disk facing properly but what should be the rotation? I remember this can be calculated using Quaternions but I can't seem to remember the math.

A: 

Quaternions describe a rotation about an axis. <w,x,y,z> will rotate around the axis <x,y,z> some amount depending on the balance between the magnitude of w and the magnitude of the vector.

<cos θ/2, x*sin θ/2, y*sin θ/2, z*sin θ/2>, where |<x, y, z>| = 1

For example, rotating it to face the positive Y-axis instead, you need to rotate it 90° around the X-axis. The vector would be <0, 1, 0>, and the quaternion would be <cos 90°, 0, sin 90°, 0> = <0, 0, 1, 0>.

To rotate the figure from facing the positive Z-axis, to facing the vector <x,y,z> you need to find the rotation-vector and angle of rotation. To find the rotation axis, you can take the cross-product of a current vector, and where you want it to be.

If it is facing the positive Z-axis, the current vector would be <0, 0, 1>. If you want it to face <x,y,z>, the rotation-axis would be <0, 0, 1> x <x, y, z> = <-y, x, 0>, and the angle would be arctan(sqrt(x^2+y^2),z). The quaternion becomes

<cos(θ/2), -y*sin(θ/2), x*sin(θ/2), 0>, where θ = arctan(sqrt(x^2+y^2), z)
MizardX
+3  A: 

Rotation around an arbitrary axis: Given angle r in radians and unit vector u = ai + bj + ck or [a,b,c], define:

q0 = cos(r/2)  
q1 = sin(r/2) a   
q2 = sin(r/2) b  
q3 = sin(r/2) c

and construct from these values the rotation matrix:

   ( q0^2+q1^2 - q2^2 - q3^2 | 2*(q1*q2 - q0*q3)           | 2*(q1*q3 + q0*q2)         )
Q =( 2*(q2*q1 + q0*q3)       | (q0^2 - q1^2 + q2^2 - q3^2) | 2*(q2*q3 - q0*q1)         )
   ( 2*(q3*q1 - q0*q2)       | 2*(q3*q2 + q0*q1)           | q0^2 - q1^2 - q2^2 + q3^2 )

To find the rotation you need to do, you can calculate the cross product between the current vector and the target vector. You will obtain the orthogonal vector (which will be your rotation vector to create the quaternion) and the length of this vector is the sin of the angle you have to compensate so that the start and target vector overlap.

Stefano Borini
+8  A: 

The solution should be pretty straightforward, and shouldn't require quarternions.

The axis of rotation to get from Normal1 to Normal2 must be orthogonal to both, so just take their vector cross-product.

The amount of rotation is easily derived from their dot-product. This value is |A|.|B|.cos(theta), but as the two normal vectors should be normalised it will give cos(theta), so just take the inverse cosine to get the rotation amount.

The resulting vector and angle are the required parameters for glRotate() - there's no need to calculate the actual rotation matrix yourself.

p.s. don't forget that glRotate() needs the angle in degrees, but the normal C trig functions work in radians.

Alnitak
Thank you. that worked perfectly.
shoosh