17

I have a model rotated by a quaternion. I can only set the rotation, I can't add or subtract from anything. I need to get the value of an axis, and than add an angle to it (maybe a degree or radian?) and than re-add the modified quaternion.

How can I do this? (answer on each axis).

andand
  • 17,134
  • 11
  • 53
  • 79
William
  • 8,630
  • 23
  • 77
  • 110
  • 1
    I think you need to clarify a bit further. You say you can only set the rotation... A quaternion serves the same purpose as a rotation matrix. The difference being that the quaternion is numerically stable, they are more expensive to apply to 3d geometry but are less expensive for concatenation... So they are typically used when long series of rotations need to be applied and then they are transformed back to rotation matrices before application. Please clarify what you mean by answer on each axis, perhaps you want quaternion -> three rotation matrix where each is about one axis? – Quaternion Dec 14 '10 at 07:27

2 Answers2

38

You can multiply two quaternions together to produce a third quaternion that is the result of the two rotations. Note that quaternion multiplication is not commutative, meaning order matters (if you do this in your head a few times, you can see why).

You can produce a quaternion that represents a rotation by a given angle around a particular axis with something like this (excuse the fact that it is c++, not java):

Quaternion Quaternion::create_from_axis_angle(const double &xx, const double &yy, const double &zz, const double &a)
{
    // Here we calculate the sin( theta / 2) once for optimization
    double factor = sin( a / 2.0 );

    // Calculate the x, y and z of the quaternion
    double x = xx * factor;
    double y = yy * factor;
    double z = zz * factor;

    // Calcualte the w value by cos( theta / 2 )
    double w = cos( a / 2.0 );

    return Quaternion(x, y, z, w).normalize();
}

So to rotate around the x axis for example, you could create a quaternion with createFromAxisAngle(1, 0, 0, M_PI/2) and multiply it by the current rotation quaternion of your model.

sje397
  • 41,293
  • 8
  • 87
  • 103
  • 2
    So if I want to create a quaternion with a -15 degrees rotation on the X axis, I should call createFromAxisAngle(1, 0, 0, -15*PI/180)? Thanks! – VladN Oct 25 '12 at 13:15
  • 2
    @Hubrus: Yep, that'd be correct. Then, if you call that quaternion `x` and your model quaternion `y`, you would normally set the total rotation using `x * y` (depending on how you implement multiplication of course). – sje397 Oct 25 '12 at 22:44
  • Naming a variable "result" is not a good convention, it gives the impression it would be the return value. How about "factor"? – lahjaton_j Dec 10 '15 at 11:17
  • 1
    @lahjaton_j Agreed. Fixed. – sje397 Dec 11 '15 at 03:37
5

Made a runable code from sje397's post for testing other details later, thought I would share. Eventually using C the reason for no Quaternion class.

#include <iostream>
#include <math.h>
using namespace std;

struct float4{
   float x;
   float y;
   float z;
   float w;  
};
float4 make_float4(float x, float y, float z, float w){
   float4 quat = {x,y,z,w};
   return quat;
}
float dot(float4 a)
{
   return (((a.x * a.x) + (a.y * a.y)) + (a.z * a.z)) + (a.w * a.w);
}
float4 normalize(float4 q)
{
   float num = dot(q);
   float inv = 1.0f / (sqrtf(num));
   return make_float4(q.x * inv, q.y * inv, q.z * inv, q.w * inv);
}
float4 create_from_axis_angle(const float &xx, const float &yy, const float &zz, const float &a)
{
   // Here we calculate the sin( theta / 2) once for optimization
   float factor = sinf( a / 2.0f );

   float4 quat;
   // Calculate the x, y and z of the quaternion
   quat.x = xx * factor;
   quat.y = yy * factor;
   quat.z = zz * factor;

   // Calcualte the w value by cos( theta / 2 )
   quat.w = cosf( a / 2.0f );
   return normalize(quat);
}
int main()
{
   float degrees = 10.0f;
   float4 quat = create_from_axis_angle(1, 0, 0, degrees*(3.14159f/180.0f));
   cout << "> (" << quat.x << ", " <<quat.y << ", " <<quat.z << ", " <<quat.w << ")" << endl; 
   return 0;
}

Output

(0.0871557, 0, 0, 0.996195)

Greg
  • 51
  • 1
  • 1