Quaternion (Quaternary)
Definition of Quaternion
The following table lists the general definitions of the four elements:
Q = W + Xi + YJ + ZK
Where w, x, y, and z are real numbers. At the same time, there are:
I * I =-1
J * j =-1
K * k =-1
It can also be expressed:
Q = [W, V]
Where V = (x, y, z) is a vector, W is a scalar, although V is a vector, it cannot be simply understood as a vector of 3D space, it is a vector in a 4-dimensional space and is not easy to imagine.
In general, quaternion describes a rotation axis and a rotation angle. The rotation axis and the angle can be obtained through quaternion: toangleaxis conversion. Of course, you can also specify an angle and a rotation axis to construct a quaternion. This angle is relative to the unit Quaternary, or relative to the initial direction of the object.
When we multiply a vector by a single element, the vector is actually the vector generated by rotating the angle described by this element around the rotation axis described by this element.
Advantages of tuples
There are multiple ways to indicate rotation, such as axis/angle, Euler angles, matrix, and tuples. Compared with other methods, tuples have their own advantages:
There will be no gimbal lock problem in the Euler's corner in the Quaternary element.
The Quaternary element consists of four numbers, and the rotation matrix requires nine numbers.
Easier interpolation between two Enis
After multiple operations, the Quaternary and matrix will accumulate errors. normalize and orthogonalize need to be done respectively to normalize them, making it easier to normalize them.
Similar to the rotation matrix, two tuples can be multiplied to indicate two rotations.
Basic quaternion operations
Normalizing a quaternion
// Normalising a quaternion works similar to a vector. This method will not do anything
// If the quaternion is close enough to being unit-length. Define tolerance as something
// Small like 0.20.1f to get accurate results
Void quaternion: normalise ()
{
// Don't normalize if we don't have
Float mag2 = W * w + x * x + y * Y + z * z;
If (mag2! = 0.f & (FABS (mag2-1.0f)> tolerance )){
Float mag = SQRT (mag2 );
W/= mag;
X/= mag;
Y/= mag;
Z/= mag;
}
}
The complex conjugate of a quaternion
// We need to get the inverse of a quaternion to properly apply a quaternion-rotation to a vector
// The conjugate of a quaternion is the same as the inverse, as long as the quaternion is unit-length
Quaternion quaternion: getconjugate ()
{
Return quaternion (-X,-y,-Z, W );
}
Multiplying Quaternions
// Multiplying Q1 with q2 applies the rotation Q2 to Q1
Quaternion quaternion: Operator * (const quaternion & rq) const
{
// The constructor takes its arguments as (X, Y, Z, W)
Return quaternion (w * rq. x + x * rq. W + y * rq. z-z * rq. y,
W * rq. Y + y * rq. W + z * rq. X-x * rq. Z,
W * rq. Z + z * rq. W + x * rq. Y-y * rq. X,
W * rq. W-x * rq. x-y * rq. Y-z * rq. z );
}
Rotating Vectors
// Multiplying a quaternion Q with a vector V applies the Q-rotation to V
Vector3 quaternion: Operator * (const vector3 & VEC) const
{
Vector3 VN (VEC );
Vn. normalise ();
Quaternion vecquat, resquat;
Vecquat. x = Vn. X;
Vecquat. Y = Vn. Y;
Vecquat. z = Vn. Z;
Vecquat. W = 0.0f;
Resquat = vecquat * getconjugate ();
Resquat = * This * resquat;
Return (vector3 (resquat. X, resquat. Y, resquat. z ));
}
How to convert to/from quaternions1
Quaternion from Axis-Angle
// Convert from axis angle
Void quaternion: fromaxis (const vector3 & V, float angle)
{
Float sinangle;
Angle * = 0.5f;
Vector3 VN (v );
Vn. normalise ();
Sinangle = sin (angle );
X = (VN. x * sinangle );
Y = (VN. y * sinangle );
Z = (VN. z * sinangle );
W = cos (angle );
}
Quaternion from Euler Angles
// Convert from Euler Angles
Void quaternion: fromeitch (float pitch, float yaw, float roll)
{
// Basically we create 3 quaternions, one for pitch, one for yaw, one for Roll
// And multiply those together.
// The calculation below does the same, just shorter
Float P = pitch * piover180/2.0;
Float y = yaw * piover180/2.0;
Float r = roll * piover180/2.0;
Float sinp = sin (P );
Float Siny = sin (y );
Float SINR = sin (R );
Float COSP = cos (P );
Float cosy = cos (y );
Float cosr = cos (R );
This-> X = SINR * COSP * cosy-cosr * sinp * Siny;
This-> Y = cosr * sinp * cosy + SINR * COSP * Siny;
This-> Z = cosr * COSP * Siny-SINR * sinp * cosy;
This-> W = cosr * COSP * cosy + SINR * sinp * Siny;
Normalise ();
}
Quaternion to Matrix
// Convert to Matrix
Matrix4 quaternion: getmatrix () const
{
Float X2 = x * X;
Float y2 = y * Y;
Float Z2 = z * z;
Float xy = x * Y;
Float xz = x * z;
Float YZ = y * z;
Float wx = W * X;
Float WY = W * Y;
Float WZ = W * z;
// This calculation wocould be a lot more complicated for non-unit length Quaternions
// Note: the constructor of matrix4 expects the matrix in column-Major format like expected
// OpenGL
Return matrix4 (1.0f-2.0f * (y2 + Z2), 2.0f * (xy-WZ), 2.0f * (xz + WY), 0.0f,
2.0f * (xy + WZ), 1.0f-2.0f * (X2 + Z2), 2.0f * (YZ-wx), 0.0f,
2.0f * (xz-WY), 2.0f * (yz + wx), 1.0f-2.0f * (X2 + y2), 0.0f,
0.0f, 0.0f, 0.0f, 1.0f)
}
Quaternion to axis-Angle
// Convert to axis/angles
Void quaternion: getaxisangle (vector3 * axis, float * angle)
{
Float scale = SQRT (x * x + y * Y + z * z );
Axis-> X = x/scale;
Axis-> Y = y/scale;
Axis-> Z = z/scale;
* Angle = ACOs (W) * 2.0f;
}
Quaternion Interpolation
Linear interpolation
The simplest interpolation algorithm is linear interpolation. The formula is as follows:
Q (t) = (1-T) Q1 + T Q2
However, this result needs to be normalized, otherwise the unit length of q (t) will change, so
Q (t) = (1-T) Q1 + T Q2/| (1-T) Q1 + T Q2 |
Spherical linear interpolation
Although linear interpolation is very effective, it cannot describe the curve between Q1 and Q2 at a constant rate. This is also a drawback. We need to find a method to make Q1-> q (t) the angle θ between them is linear, that is, θ (t) = (1-T) θ 1 + T * θ 2. In this way, we obtain the spherical linear interpolation function q (t), as shown below:
Q (t) = Q1 * sin θ (1-T)/sin θ + q2 * sin θ t/sine θ
If you use d3d, you can use the d3dxquaternionslerp function to complete the interpolation process.
Implement camera rotation using Quaternion
In general, camera operations can be divided into the following types:
Move along a straight line
Rotate around a certain axis
Public rotation around a certain axis
The following is a camera class that uses quaternion:
Class camera {
PRIVATE:
Quaternion m_orientation;
Public:
Void rotate (const quaternion & Q );
Void rotate (const vector3 & axis, const radian & angle );
Void roll (const glfloat angle );
Void yaw (const glfloat angle );
Void pitch (const glfloat angle );
};
Void camera: Rotate (const quaternion & Q)
{
// Note the order of the mult, I. e. Q comes after
M_orientation = Q * m_orientation;
}
Void camera: Rotate (const vector3 & axis, const radian & angle)
{
Quaternion Q;
Q. fromangleaxis (angle, axis );
Rotate (Q );
}
Void camera: Roll (const glfloat angle) // In radian
{
Vector3 zaxis = m_orientation * vector3: unit_z;
Rotate (zaxis, angleinradian );
}
Void camera: yaw (const glfloat angle) // in degree
{
Vector3 yaxis;
{
// Rotate around local Y axis
Yaxis = m_orientation * vector3: unit_y;
}
Rotate (yaxis, angleinradian );
}
Void camera: pitch (const glfloat angle) // In radian
{
Vector3 xaxis = m_orientation * vector3: unit_x;
Rotate (xaxis, angleinradian );
}
Void camera: glulookat (){
Glfloat M [4] [4];
Identf (& M [0] [0]);
M_orientation.creatematrix (& M [0] [0]);
Glmultmatrixf (& M [0] [0]);
Gltranslatef (-m_eyex,-m_eyey,-m_eyez );
}
Use quaternion to implement trackball
Drag an object with the mouse to rotate in a three-dimensional space. Generally, a trackball is designed, and the internal implementation of the ball is also commonly used as the Quaternary element.
Class trackball
{
Public:
Trackball ();
Void push (const qpointf & P );
Void move (const qpointf & P );
Void release (const qpointf & P );
Qquaternion rotation () const;
PRIVATE:
Qquaternion m_rotation;
Qvector3d m_axis;
Float m_angularvelocity;
Qpointf m_lastpos;
};
Void trackball: Move (const qpointf & P)
{
If (! M_pressed)
Return;
Qvector3d lastpos3d = qvector3d (m_lastpos.x (), m_lastpos.y (), 0.0f );
Float sqrz = 1-qvector3d: dotproduct (lastpos3d, lastpos3d );
If (sqrz> 0)
Lastpos3d. setz (SQRT (sqrz ));
Else
Lastpos3d. normalize ();
Qvector3d currentpos3d = qvector3d (p. x (), P. Y (), 0.0f );
Sqrz = 1-qvector3d: dotproduct (currentpos3d, currentpos3d );
If (sqrz> 0)
Currentpos3d. setz (SQRT (sqrz ));
Else
Currentpos3d. normalize ();
M_axis = qvector3d: crossproduct (lastpos3d, currentpos3d );
Float angle = 180/pI * asin (SQRT (qvector3d: dotproduct (m_axis, m_axis )));
M_axis.normalize ();
M_rotation = qquaternion: fromaxisandangle (m_axis, angle) * m_rotation;
M_lastpos = P;
}
Bytes ---------------------------------------------------------------------------------------------------------
Each unit of the Quaternary can correspond to a rotating matrix.
Unit: q = (S, V). The concatenation is Q * = (S,-V)
Modulo | q | = 1;
Inverse Q ^ (-1) = Q */(| q |) = Q * of Q = (S, V *
Which of the following is a vector R that rotates the Angle Along the vector N (assuming v)? This can be easily done using the Quaternary element.
Construct two element Q = (COS (A/2), sin (A/2) * n), P = (0, R)
P' = Q * p * q ^ (-1). This can ensure that the obtained P' is in the (0, R') format,The obtained R' is the vector After r rotation.
In addition, the Q * p * q ^ (-1) operation on P is equivalent to multiplying P by a rotating matrix. Here we assume that Q = (COS (A/2 ), sin (A/2) * n) = (S, (x, y, z ))
Multiplication of two Enis also indicates a rotation.
Q1 * Q2 indicates that the image is first rotated by Q2 and then by Q1.
The matrix is
LikewiseA rotating matrix can also be converted into a single Quaternary matrix., That is, you can obtain the (S, x, y, z) element of the rotation matrix,
The method is:
Given any unit axis Q (Q1, q2, Q3) (vector), evaluate the vector p (x, y, z) (or point P) rao Q rotates the new vector p '(or point P') after the theta angle Transformation '):
1. Use the following tool:
-------------------------------------------------------------------------
Conclusion: The structure of the Quaternary transform p '= Q * p * q-1, (p, q is expanded from the vector p, q ). Then, the p 'to the corresponding vector (or point) is the transformed new vector p' (or point P ').
Where, p ', Q, P, Q-1 are all Quaternary. Q is expanded by vector Q, Which is Q = (COS (theta/2), sin (theta/2) * q), P is extended by vector p, Which is P = (0, x, Y, Z), the Q-1 is the inverse of Q, because Q is the unit of Quaternary, so Q-1 = Q * = (COS (theta/2),-sin (theta/2) * q ).
Http://www.linuxgraphics.cn/opengl/opengl_quaternion.html
Http://blog.csdn.net/qq960885333/article/details/8191448
Http://blog.csdn.net/jiexuan357/article/details/7727634