Professional Documents
Culture Documents
http://content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quatern...
From GPWiki
Contents
1 Foreword and warning 2 Just what is a quaternion? 3 Why use quaternions 4 Why quaternions are neat 5 Some basic quaternion operations 5.1 Normalizing a quaternion 5.2 The complex conjugate of a quaternion 5.3 Multiplying quaternions 5.4 Rotating vectors 6 How to convert to/from quaternions 6.1 Quaternion from axis-angle 6.2 Quaternion from Euler angles 6.3 Quaternion to Matrix 6.4 Quaternion to axis-angle 7 Example
1z8
2013-10-28 22:53
http://content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quatern...
Identity (no rotation) 180 degrees about x-axis 180 degrees about y-axis 180 degrees about z-axis angle , axis (unit vector)
1, -1 i, -i j, -j k, -k
2z8
2013-10-28 22:53
http://content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quatern...
Just like with the Mobius strip, I can solve the problem with a double-cover. I simply duplicate each possible 3D rotation; one copy gets a "positive" quaternion, and the other copy gets a "negative" quaternion. The resulting representation is equivalent to a 3-sphere in 4D hyper-space, it's orientable, and it completely avoids the problem of singularities and discontinuities.
Normalizing a quaternion
// normalising a quaternion works similar to a vector. This method will not // if the quaternion is close enough to being unit-length. define TOLERANCE // small like 0.00001f to get accurate results void Quaternion::normalise() { // Don't normalize if we don't have to float mag2 = w * w + x * x + y * y + z * z; if (fabs(mag2) > TOLERANCE && fabs(mag2 - 1.0f) > TOLERANCE) { float mag = sqrt(mag2); w /= mag; x /= mag; y /= mag; z /= mag; } }
// We need to get the inverse of a quaternion to properly apply a quaternion // The conjugate of a quaternion is the same as the inverse, as long as the Quaternion Quaternion::getConjugate() { return Quaternion(-x, -y, -z, w); }
Multiplying quaternions
To multiply two quaternions, write each one as the sum of a scalar and a vector. The product of and is where
// 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)
3z8
2013-10-28 22:53
http://content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quatern...
return Quaternion(w w w w }
* * * *
+ + + -
x y z x
* * * *
+ + + -
y z x y
* * * *
z x y z
* * * *
Please note: Quaternion-multiplication is NOT commutative. Thus q1 * q2 is not the same as q2 * q1. This is pretty obvious actually: As I explained, quaternions represent rotations and multiplying them "concatenates" the rotations. Now take you hand and hold it parallel to the floor so your hand points away from you. Rotate it 90 around the x-axis so it is pointing upward. Now rotate it 90 clockwise around its local y-axis (the one coming out of the back of your hand). Your hand should now be pointing to your right, with you looking at the back of your hand. Now invert the rotations: Rotate your hand around the y-axis so its facing right with the back of the hand facing upwards. Now rotate around the x axis and your hand is pointing up, back of hand facing your left. See, the order in which you apply rotations matters. Ok, ok, you probably knew that...
Rotating vectors
To apply a quaternion-rotation to a vector, you need to multiply the vector by the quaternion and its conjugate.
// 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)); }
4z8
2013-10-28 22:53
http://content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quatern...
// 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 y z w } = = = = (vn.x * sinAngle); (vn.y * sinAngle); (vn.z * sinAngle); cos(angle);
// Convert from Euler Angles void Quaternion::FromEuler(float pitch, float yaw, float roll) { // Basically we create 3 Quaternions, one for pitch, one for yaw, on // 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 float float float float float sinp siny sinr cosp cosy cosr = = = = = = = = = = sin(p); sin(y); sin(r); cos(p); cos(y); cos(r); * * * * cosp sinp cosp cosp * * * * cosy cosy siny cosy + + cosr sinr sinr sinr * * * * sinp cosp sinp sinp * * * * siny; siny; cosy; siny;
normalise(); }
Quaternion to Matrix
5z8
2013-10-28 22:53
http://content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quatern...
// 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 would be a lot more complicated for non-unit len // Note: The constructor of Matrix4 expects the Matrix in column-maj // OpenGL return Matrix4( 1.0f - 2.0f * (y2 + z2), 2.0f * (xy - wz), 2.0f 2.0f * (xy + wz), 1.0f - 2.0f * (x2 + z2), 2.0f 2.0f * (xz - wy), 2.0f * (yz + wx), 1.0f - 2.0f 0.0f, 0.0f, 0.0f, 1.0f) }
Quaternion to axis-angle
Given a quaternion , the (non-normalized) rotation axis is simply , provided that an axis exists. For very small rotations, gets close to the zero vector, so when we compute the normalized rotation axis, the calculation may blow up. In particular, the identity rotation has , so the rotation axis is undefined. To find the angle of rotation, note that w = cos( / 2) and .
// 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; }
Example
Ok, with the above Quaternion class, It's very simple to create a camera class that has one such Quaternion to represent its orientation: void Camera::movex(float xmmod) {
6z8
2013-10-28 22:53
http://content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quatern...
pos += rotation * Vector3(xmmod, 0.0f, 0.0f); } void Camera::movey(float ymmod) { pos.y -= ymmod; } void Camera::movez(float zmmod) { pos += rotation * Vector3(0.0f, 0.0f, -zmmod); } void Camera::rotatex(float xrmod) { Quaternion nrot(Vector3(1.0f, 0.0f, 0.0f), xrmod * PIOVER180); rotation = rotation * nrot; } void Camera::rotatey(float yrmod) { Quaternion nrot(Vector3(0.0f, 1.0f, 0.0f), yrmod * PIOVER180); rotation = nrot * rotation; } void Camera::tick(float seconds) { if (xrot != 0.0f) rotatex(xrot * seconds * rotspeed); if (yrot != 0.0f) rotatey(yrot * seconds * rotspeed); if (xmov != 0.0f) movex(xmov * seconds * movespeed); if (ymov != 0.0f) movey(ymov * seconds * movespeed); if (zmov != 0.0f) movez(zmov * seconds * movespeed); } In this code, xrot, yrot, xmov, ymov and zmod are floats representing how fast the player wants to rotate/move around/on this axis. "seconds" is the time passed since the last call to tick. rotspeed and movespeed represent how fast the camera can rotate or move. PIOVER180 is defined as pi/180, so multiplying with it converts from degrees to radians. You might be wondering why in rotatex we multiply "rotation * nrot" and in rotatey "nrot * rotation". As I said, multiplication is not commutative. The first rotates the existing quaternion around x (looking up and down), the second rotates an upward-quaternion around the existing rotation. This way, we look left/right around the global y-axis, while rotation up/down is around the local x-axis. This is the behaviour you have in a 3D shooter. Try to change the order of rotations to see what happens. Retrieved from "http://content.gpwiki.org/index.php /OpenGL:Tutorials:Using_Quaternions_to_represent_rotation" Categories: OpenGL | Tutorial This page was last modified on 26 September 2011, at 10:29. This page has been accessed 87,809 times.
7z8
2013-10-28 22:53
http://content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quatern...
8z8
2013-10-28 22:53