4
\$\begingroup\$

I have a unit vector (direction) which represent a rotation around Y axis,

and would like to turn this into a quaternion to represent the same rotation.

I use GLM library, but interested any library agnostic solution.

Should I pack the direction vector to a matrix then convert to quaternion? Is there a better way?

UPDATE: As I said the matrix solution works already, but there must be a shorter way, as i don't need all dimension only one (around Y):

glm::quat q = glm::conjugate(glm::toQuat(
    glm::lookAt(glm::vec3(from.getX(), from.getY(), from.getZ()),
                glm::vec3(from.getX(), from.getY(), from.getZ()) + direction,
                glm::vec3(0, 1, 0)
    )
));
\$\endgroup\$
2
  • \$\begingroup\$ Is the unit vector in the xz plane? (ie. y = 0, only its x & z components might be non-zero) \$\endgroup\$
    – DMGregory
    Commented Sep 30, 2017 at 21:16
  • \$\begingroup\$ @DMGregory yes, correct \$\endgroup\$
    – Avi
    Commented Oct 1, 2017 at 19:41

2 Answers 2

6
\$\begingroup\$

From http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/

qx = ax * sin(angle/2)
qy = ay * sin(angle/2)
qz = az * sin(angle/2)
qw = cos(angle/2)

But since your vector represents the rotation, and is not the axis of rotation, we need to compute the angle. Your axis of rotation is just 0,1,0

angle = atan2( vector.x, vector.z ) // Note: I expected atan2(z,x) but OP reported success with atan2(x,z) instead! Switch around if you see 90° off.
qx = 0
qy = 1 * sin( angle/2 )
qz = 0
qw = cos( angle/2 )

NOTE: this even works for non-unit vectors, as atan2 will compute the correct angle for any length vector, as long as it is not zero.

\$\endgroup\$
7
  • \$\begingroup\$ We can do one better and skip computing the angle directly, by noting that x & z already encode the sine and cosines we need to use the half-angle trig identities... \$\endgroup\$
    – DMGregory
    Commented Sep 30, 2017 at 23:29
  • \$\begingroup\$ If you know cos(a), how does that help you to know cos(a/2) then, without doing cos( acos(a) / 2 ) which seems costly, and also would require unit vector. \$\endgroup\$
    – Bram
    Commented Oct 1, 2017 at 3:31
  • \$\begingroup\$ By using the half-angle trig identities I mention in the second half of my comment. eg. cos(a/2) = sqrt((1 + cos(a))/2) for angles in the range (-π...+π) — still need two square roots instead of two trig and one inverse trig, so it's not a gigantic savings by any means, it's just kind if a cute pattern. ;) \$\endgroup\$
    – DMGregory
    Commented Oct 1, 2017 at 10:35
  • \$\begingroup\$ @Bram thanks, it works and much shorter than my matrix solution ... one minor thing: if i rotate a model with the resulting quaternion, it seems to always add extra 90 to the correct rotation... my model is originally facing +Z axis ... but i can fix that substracting a constant -90 degree \$\endgroup\$
    – Avi
    Commented Oct 1, 2017 at 19:48
  • 1
    \$\begingroup\$ @Bram that did it, angle should be angle = atan2( vector.x, vector.z) ... thank you very much, this is a beautiful whole answer! \$\endgroup\$
    – Avi
    Commented Oct 2, 2017 at 18:06
0
\$\begingroup\$

Try to look in this way:

Vector3df sundirection = sun->GetDirectionTo();
Vector3df rotaxis = normalize( cross(Vector3df(1, 0, 0), sundirection));
double angle = acos(dot(Vector3df(1,0,0),sundirection));
Quaterniondf orient(angle, rotaxis);
\$\endgroup\$
1
  • \$\begingroup\$ Since the rotation axis is already known in this question, you can save yourself a step. :) You'll want to use atan2 instead of acos though, to ensure you get the correct angle for all 360 degrees of possibilities (acos can only distinguish 180 degrees of possible angles, the other angles have the same cosine as another angle we've already counted) \$\endgroup\$
    – DMGregory
    Commented Apr 20, 2020 at 16:46

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .