Convert Quaternion rotation to rotation matrix? - opengl

Basically, given a quaterion (qx, qy, qz, qw)... How can i convert that to an OpenGL rotation matrix? I'm also interested in which matrix row is "Up", "Right", "Forward" etc... I have a camera rotation in quaternion that I need in vectors...

The following code is based on a quaternion (qw, qx, qy, qz), where the order is based on the Boost quaternions:
boost::math::quaternion<float> quaternion;
float qw = quaternion.R_component_1();
float qx = quaternion.R_component_2();
float qy = quaternion.R_component_3();
float qz = quaternion.R_component_4();
First you have to normalize the quaternion:
const float n = 1.0f/sqrt(qx*qx+qy*qy+qz*qz+qw*qw);
qx *= n;
qy *= n;
qz *= n;
qw *= n;
Then you can create your matrix:
Matrix<float, 4>(
1.0f - 2.0f*qy*qy - 2.0f*qz*qz, 2.0f*qx*qy - 2.0f*qz*qw, 2.0f*qx*qz + 2.0f*qy*qw, 0.0f,
2.0f*qx*qy + 2.0f*qz*qw, 1.0f - 2.0f*qx*qx - 2.0f*qz*qz, 2.0f*qy*qz - 2.0f*qx*qw, 0.0f,
2.0f*qx*qz - 2.0f*qy*qw, 2.0f*qy*qz + 2.0f*qx*qw, 1.0f - 2.0f*qx*qx - 2.0f*qy*qy, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
Depending on your matrix class, you might have to transpose it before passing it to OpenGL.

One way to do it, which is pretty easy to visualize, is to apply the rotation specified by your quaternion to the basis vectors (1,0,0), (0,1,0), and (0,0,1). The rotated values
give the basis vectors in the rotated system relative to the original system. Use these
vectors to form the rows of the rotation matrix. The resulting matrix, and its transpose,
represent the forward and inverse transformations between the original system and the
rotated system.
I'm not familiar with the conventions used by OpenGL, so maybe someone else can answer
that part of your question...

You might not have to deal with a rotation matrix at all. Here is a way that appears to be faster than converting to a matrix and multiplying a vector with it:
// move vector to camera position co (before or after rotation depending on the goal)
v -= co;
// rotate vector v by quaternion q; see info [1]
vec3 t = 2 * cross(q.xyz, v);
v = v + q.w * t + cross(q.xyz, t);
[1] http://mollyrocket.com/forums/viewtopic.php?t=833&sid=3a84e00a70ccb046cfc87ac39881a3d0

using glm, you can simply use a casting operator.
so to convert from a matrix4 to quaternion, simply write
glm::mat4_cast(quaternion_name)

Related

How to rotate a vector by a given direction

I'm creating some random vectors/directions in a loop as a dome shape like this:
void generateDome(glm::vec3 direction)
{
for(int i=0;i<1000;++i)
{
float xDir = randomByRange(-1.0f, 1.0f);
float yDir = randomByRange(0.0f, 1.0f);
float zDir = randomByRange(-1.0f, 1.0f);
auto vec = glm::vec3(xDir, yDir, zDir);
vec = glm::normalize(vec);
...
//some transformation with direction-vector
}
...
}
This creates vectors as a dome-shape in +y direction (0,1,0):
Now I want to rotate the vec-Vector by a given direction-Vector like (1,0,0).
This should rotate the "dome" to the x-direction like this:
How can I achieve this? (preferably with glm)
A rotation is generally defined using some sort of offset (axis-angle, quaternion, euler angles, etc) from a starting position. What you are looking for would be more accurately described (in my opinion) as a re-orientation. Luckily this isn't too hard to do. What you need is a change-of-basis matrix.
First, lets just define what we're working with in code:
using glm::vec3;
using glm::mat3;
vec3 direction; // points in the direction of the new Y axis
vec3 vec; // This is a randomly generated point that we will
// eventually transform using our base-change matrix
To calculate the matrix, you need to create unit vectors for each of the new axes. From the example above it becomes apparent that you want the vector provided to become the new Y-axis:
vec3 new_y = glm::normalize(direction);
Now, calculating the X and Z axes will be a tad more complicated. We know that they must be orthogonal to each other and to the Y axis calculated above. The most logical way to construct the Z axis is to assume that the rotation is taking place in the plane defined by the old Y axis and the new Y axis. By using the cross-product we can calculate this plane's normal vector, and use that for the Z axis:
vec3 new_z = glm::normalize(glm::cross(new_y, vec3(0, 1, 0)));
Technically the normalization isn't necessary here since both input vectors are already normalized, but for the sake of clarity, I've left it. Also note that there is a special case when the input vector is colinear with the Y-axis, in which case the cross product above is undefined. The easiest way to fix this is to treat it as a special case. Instead of what we have so far, we'd use:
if (direction.x == 0 && direction.z == 0)
{
if (direction.y < 0) // rotate 180 degrees
vec = vec3(-vec.x, -vec.y, vec.z);
// else if direction.y >= 0, leave `vec` as it is.
}
else
{
vec3 new_y = glm::normalize(direction);
vec3 new_z = glm::normalize(glm::cross(new_y, vec3(0, 1, 0)));
// code below will go here.
}
For the X-axis, we can cross our new Y-axis with our new Z-axis. This yields a vector perpendicular to both of the others axes:
vec3 new_x = glm::normalize(glm::cross(new_y, new_z));
Again, the normalization in this case is not really necessary, but if y or z were not already unit vectors, it would be.
Finally, we combine the new axis vectors into a basis-change matrix:
mat3 transform = mat3(new_x, new_y, new_z);
Multiplying a point vector (vec3 vec) by this yields a new point at the same position, but relative to the new basis vectors (axes):
vec = transform * vec;
Do this last step for each of your randomly generated points and you're done! No need to calculate angles of rotation or anything like that.
As a side note, your method of generating random unit vectors will be biased towards directions away from the axes. This is because the probability of a particular direction being chosen is proportional to the distance to the furthest point possible in a given direction. For the axes, this is 1.0. For directions like eg. (1, 1, 1), this distance is sqrt(3). This can be fixed by discarding any vectors which lie outside the unit sphere:
glm::vec3 vec;
do
{
float xDir = randomByRange(-1.0f, 1.0f);
float yDir = randomByRange(0.0f, 1.0f);
float zDir = randomByRange(-1.0f, 1.0f);
vec = glm::vec3(xDir, yDir, zDir);
} while (glm::length(vec) > 1.0f); // you could also use glm::length2 instead, and avoid a costly sqrt().
vec = glm::normalize(vec);
This would ensure that all directions have equal probability, at the cost that if you're extremely unlucky, the points picked may lie outside the unit sphere over and over again, and it may take a long time to generate one that's inside. If that's a problem, it could be modified to limit the iterations: while (++i < 4 && ...) or by increasing the radius at which a point is accepted every iteration. When it is >= sqrt(3), all possible points would be considered valid, so the loop would end. Both of these methods would result in a slight biasing away from the axes, but in almost any real situation, it would not be detectable.
Putting all the code above together, combined with your code, we get:
void generateDome(glm::vec3 direction)
{
// Calculate change-of-basis matrix
glm::mat3 transform;
if (direction.x == 0 && direction.z == 0)
{
if (direction.y < 0) // rotate 180 degrees
transform = glm::mat3(glm::vec3(-1.0f, 0.0f 0.0f),
glm::vec3( 0.0f, -1.0f, 0.0f),
glm::vec3( 0.0f, 0.0f, 1.0f));
// else if direction.y >= 0, leave transform as the identity matrix.
}
else
{
vec3 new_y = glm::normalize(direction);
vec3 new_z = glm::normalize(glm::cross(new_y, vec3(0, 1, 0)));
vec3 new_x = glm::normalize(glm::cross(new_y, new_z));
transform = mat3(new_x, new_y, new_z);
}
// Use the matrix to transform random direction vectors
vec3 point;
for(int i=0;i<1000;++i)
{
int k = 4; // maximum number of direction vectors to guess when looking for one inside the unit sphere.
do
{
point.x = randomByRange(-1.0f, 1.0f);
point.y = randomByRange(0.0f, 1.0f);
point.z = randomByRange(-1.0f, 1.0f);
} while (--k > 0 && glm::length2(point) > 1.0f);
point = glm::normalize(point);
point = transform * point;
// ...
}
// ...
}
You need to create a rotation matrix. Therefore you need a identity Matrix. Create it like this with
glm::mat4 rotationMat(1); // Creates a identity matrix
Now your can rotate the vectorspacec with
rotationMat = glm::rotate(rotationMat, 45.0f, glm::vec3(0.0, 0.0, 1.0));
This will rotate the vectorspace by 45.0 degrees around the z-axis (as shown in your screenshot). Now your almost done. To rotate your vec you can write
vec = glm::vec3(rotationMat * glm::vec4(vec, 1.0));
Note: Because you have a 4x4 matrix you need a vec4 to multiply it with the matrix. Generally it is a good idea always to use vec4 when working with OpenGL because vectors in smaller dimension will be converted to homogeneous vertex coordinates anyway.
EDIT: You can also try to use GTX Extensions (Experimental) by including <glm/gtx/rotate_vector.hpp>
EDIT 2: When you want to rotate the dome "towards" a given direction you can get your totation axis by using the cross-product between the direction and you "up" vector of the dome. Lets say you want to rotate the dome "toward" (1.0, 1.0, 1.0) and the "up" direction is (0.0, 1.0, 0.0) use:
glm::vec3 cross = glm::cross(up, direction);
glm::rotate(rotationMat, 45.0f, cross);
To get your rotation matrix. The cross product returns a vector that is orthogonal to "up" and "direction" and that's the one you want to rotate around. Hope this will help.

Rotate geometry to align to a direction vector

I've been trying to get my generated geometry to align with a direction vector. To illustrate what my current problem is:
A = Correctly aligned geometry ( just a triangle for testing )
B = Incorrectly aligned geometry
My current solution in code for this triangle example (This code is run for all the nodes you see on screen starting at the split, I am using the GLM math library):
glm::vec3 v1, v2, v3;
v1.x = -0.25f;
v1.z = -0.25f;
v2.x = 0.25f;
v2.z = -0.25f;
v3.x = 0.0f;
v3.z = 0.25f;
v1.y = 0.0f;
v2.y = 0.0f;
v3.y = 0.0f;
glm::mat4x4 translate = glm::translate(glm::mat4x4(1.0f), sp.position);
glm::mat4x4 rotate = glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), sp.direction, glm::vec3(0.0f, 1.0f, 0.0f));
v1 = glm::vec4(translate * rotate * glm::vec4(v1, 1.0f)).swizzle(glm::comp::X, glm::comp::Y, glm::comp::Z);
v2 = glm::vec4(translate * rotate * glm::vec4(v2, 1.0f)).swizzle(glm::comp::X, glm::comp::Y, glm::comp::Z);
v3 = glm::vec4(translate * rotate * glm::vec4(v3, 1.0f)).swizzle(glm::comp::X, glm::comp::Y, glm::comp::Z);
The direction vector values for point A:
x 0.000000000 float
y 0.788205445 float
z 0.615412235 float
The direction vector values for point B:
x 0.0543831661 float
y 0.788205445 float
z -0.613004684 float
Edit 1 (24/11/2013 # 20:36):
A and B do not have any relation, both are generated separately. When generating A or B only a position and direction is known.
I've been looking at solutions posted here:
Quaternions, rotate a model and align with a direction
Direct3D
Rotation Matrix from Vector and vice-versa
Direction Vector To
Rotation Matrix
But I haven't been able to successfully rotate my geometry to align with my direction vector. I feel like I'm doing something rather basic wrong.
Any help would be greatly appreciated!
If A and B are unit vectors and you want a rotation matrix R that transforms B so that it aligns with A, then start by computing C = B x A (the cross-product of B and A). C is the axis of rotation, and arcsin(|C|) is the necessary rotation angle.
From these you can build the required rotation matrix. It looks like glm has support for this, so I won't explain further.
NB if you are doing many, many of these in performance-critical code, you can gain a bit of speed by noting |C| = sin(theta), sqrt(1 - |C|^2) = cos(theta) and computing the matrix yourself with these known values of sin(theta) and cos(theta). For this see for example this discussion. The glm routine will take your angle arcsin(|C|) and proceed immediately to compute its sin and cos, a small waste since you already knew these and the operations are relatively expensive.
If the rotation is about some point p other than the origin, then let T be a translation that takes p to the origin, and find X = T^-1 R T. This X will be the transformation you want.

Calculating The Camera View Matrix

I have an issue where my camera seems to orbit the origin. It makes me think that I have my matrix multiplication backwards. However it seems correct to me, and if I reverse it it sorts out the orbit issue, but I get an other issue where I think my translation is backwards.
glm::mat4 Camera::updateDelta(const float *positionVec3, const float *rotationVec3)
{
// Rotation Axis
const glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
const glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
const glm::vec3 zAxis(0.0f, 0.0f, 1.0f); // Should this be -1?
// Accumulate Rotations
m_rotation.x += rotationVec3[0]; // pitch
m_rotation.y += rotationVec3[1]; // yaw
m_rotation.z += rotationVec3[2]; // roll
// Calculate Rotation
glm::mat4 rotViewMat;
rotViewMat = glm::rotate(rotViewMat, m_rotation.x, xAxis);
rotViewMat = glm::rotate(rotViewMat, m_rotation.y, yAxis);
rotViewMat = glm::rotate(rotViewMat, m_rotation.z, zAxis);
// Updated direction vectors
m_forward = glm::vec3(rotViewMat[0][2], rotViewMat[1][2], rotViewMat[2][2]);
m_up = glm::vec3(rotViewMat[0][1], rotViewMat[1][1], rotViewMat[2][1]);
m_right = glm::vec3(rotViewMat[0][0], rotViewMat[1][0], rotViewMat[2][0]);
m_forward = glm::normalize(m_forward);
m_up = glm::normalize(m_up);
m_right = glm::normalize(m_right);
// Calculate Position
m_position += (m_forward * positionVec3[2]);
m_position += (m_up * positionVec3[1]);
m_position += (m_right * positionVec3[0]);
m_position += glm::vec3(positionVec3[0], positionVec3[1], positionVec3[2]);
glm::mat4 translateViewMat;
translateViewMat = glm::translate(translateViewMat, m_position);
// Calculate view matrix.
//m_viewMat = rotViewMat * translateViewMat;
m_viewMat = translateViewMat * rotViewMat;
// Return View Proj
return m_projMat * m_viewMat;
}
Everywhere else I do the matrix multiplication in reverse which gives me the correct answer, but this function seems to want the reverse.
When calculating an objects position in 3D space I do this
m_worldMat = transMat * rotMat * scale;
Which works as expected.
There seem to be a few wrong things with the code.
One: rotViewMat is used in the first rotate call before it is initialized. What value does it get initially? Is it a unit matrix?
Two: Rotation does not have the mathematical properties you seem to assume. Rotating around x, then around y, then around z (each at a constant velocity) is not the same as rotating around any axis ("orbiting"), it is a weird wobble. Because of subsequent rotations, your accumulated x rotation ("pitch") for example may actually be causing a movement in an entirely different direction (consider what happens to x when the accumulated y rotation is close to 90 deg). Another way of saying that is rotation is non-commutative, see:
https://physics.stackexchange.com/questions/48345/non-commutative-property-of-rotation and
https://physics.stackexchange.com/questions/10362/how-does-non-commutativity-lead-to-uncertainty/10368#10368.
See also: http://en.wikipedia.org/wiki/Rotation_matrix#Sequential_angles and http://en.wikipedia.org/wiki/Euler_angles. Since Euler angles (roll-pitch-yaw) are not vectors, it doesn't make sense to add to them a velocity vector.
What you probably want is this:
glm::mat4 Camera::updateDelta(const float *positionVec3, const float *axisVec3, const float angularVelocity)
{
...
glm::mat4 rotViewMat; // initialize to unit matrix
rotViewMat = glm::rotate(rotViewMat, angularVelocity, axisVec3);
...

C++ OpenGL Quaternion for Camera flips it upside down

It does look at the target when I move to the target to the right and looks at it up until it goes 180 degrees of the -zaxis and decides to go the other way.
Matrix4x4 camera::GetViewMat()
{
Matrix4x4 oRotate, oView;
oView.SetIdentity();
Vector3 lookAtDir = m_targetPosition - m_camPosition;
Vector3 lookAtHorizontal = Vector3(lookAtDir.GetX(), 0.0f, lookAtDir.GetZ());
lookAtHorizontal.Normalize();
float angle = acosf(Vector3(0.0f, 0.0f, -1.0f).Dot(lookAtHorizontal));
Quaternions horizontalOrient(angle, Vector3(0.0f, 1.0f, 0.0f));
ori = horizontalOrient;
ori.Conjugate();
oRotate = ori.ToMatrix();
Vector3 inverseTranslate = Vector3(-m_camPosition.GetX(), -m_camPosition.GetY(), -m_camPosition.GetZ());
oRotate.Transform(inverseTranslate);
oRotate.Set(0, 3, inverseTranslate.GetX());
oRotate.Set(1, 3, inverseTranslate.GetY());
oRotate.Set(2, 3, inverseTranslate.GetZ());
oView = oRotate;
return oView;
}
As promised, a bit of code showing the way I'd make a camera look at a specific point in space at all times.
First of all, we'd need a method to construct a quaternion from an angle and an axis, I happen to have that on pastebin, the angle input is in radians:
http://pastebin.com/vLcx4Qqh
Make sure you don't input the axis (0,0,0), which wouldn't make any sense whatsoever.
Now the actual update method, we need to get the quaternion rotating the camera from default orientation to pointing towards the target point. PLEASE note I just wrote this out of the top of my head, it probably needs a little debugging and may need a little optimization, but this should at least give you a push in the right direction.
void camera::update()
{
// First get the direction from the camera's position to the target point
vec3 lookAtDir = m_targetPoint - m_position;
// I'm going to divide the vector into two 'components', the Y axis rotation
// and the Up/Down rotation, like a regular camera would work.
// First to calculate the rotation around the Y axis, so we zero out the y
// component:
vec3 lookAtHorizontal = vec3(lookAtDir.x, 0.0f, lookAtDir.z).normalize();
// Get the quaternion from 'default' direction to the horizontal direction
// In this case, 'default' direction is along the -z axis, like most OpenGL
// programs. Make sure the projection matrix works according to this.
float angle = acos(vec3(0.0f, 0.0f, -1.0f).dot(lookAtHorizontal));
quaternion horizontalOrient(angle, vec3(0.0f, 1.0f, 0.0f));
// Since we already stripped the Y component, we can simply get the up/down
// rotation from it as well.
angle = acos(lookAtDir.normalize().dot(lookAtHorizontal));
if(angle) horizontalOrient *= quaternion(angle, lookAtDir.cross(lookAtHorizontal));
// ...
m_orientation = horizontalOrient;
}
Now to actually take m_orientation and m_position and get the world -> camera matrix
// First inverse each element (-position and inverse the quaternion),
// the position is rotated since the position within a matrix is 'added' last
// to the output vector, so it needs to account for rotation of the space.
mat3 rotationMatrix = m_orientation.inverse().toMatrix();
vec3 inverseTranslate = rotationMatrix * -m_position; // Note the minus
mat4 matrix = mat3; // just means the matrix is expanded, the last entry (bottom right of the matrix) is a 1.0f like an identity matrix would be.
// This bit is row-major in my case, you just need to set the translation of the matrix.
matrix[3] = inverseTranslate.x;
matrix[7] = inverseTranslate.y;
matrix[11] = inverseTranslate.z;
EDIT I think it should be obvious but just for completeness, .dot() takes the dot product of the vectors, .cross() takes the cross product, the object executing the method is vector A, and the parameter of the method is vector B.

Extracting three euler angles from top and bottom two 3D points/one vector of unsymmetrical object

I know two 3D-points in a line (top and bottom of an unsymmetrical object), and would like to find euler angles(rotation along x, y and z axis).
Example: Need reverse engineering of following code of OpenGL, below is the just an example to show the scenario.
//Translation
glTranslated(p1.x, p1.y, p1.z);
//Rotation
glRotatef(rot.x, 1.0f, 0.0f, 0.0f);
glRotatef(rot.y, 0.0f, 1.0f, 0.0f);
glRotatef(rot.z, 0.0f, 0.0f, 1.0f);
// Draw the object ALONG Y-AXIS
p2 = DrawMyObject(); //p2 is top of my object
Now in some situations I got only p1 and p2 and I need to know euler angles (roation along x, y and z axis). How?
This is what I tried and answer should be (Rx, Ry, Rz): (4, -3, -11),
cv::Point3d p1, p2;
p1.x = 0.0525498;
p1.y = 0.0798909;
p1.z = -1.20806;
p2.x = 0.0586557;
p2.y = 0.111226;
p2.z = -1.20587;
double dx, dy, dz;
double angle;
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
angle = std::atan2(dy, dz); angle = RAD2DEG(angle);
std::cout<<"\n atan2(dy, dz): "<<int(90 - angle);
angle = std::atan2(dx, dz); angle = RAD2DEG(angle);
std::cout<<"\n atan2(dz, dx): "<<angle;
angle = std::atan2(dy, dx); angle = RAD2DEG(angle);
std::cout<<"\n atan2(dy, dx): "<<int(angle -90);
std::cout<<std::endl;
I am not getting exactly correct answer, especially rotation along Y is not correct at all. I think p1 and p2 both lies in y-axis while rotating along y-axis so the problem is. Then what is best possible solution?
As stated in the comments you will need either a 3rd point on your object or a constant world-space vector. Be aware that using a constant vector could introduce gimbal problems depending on your specific application and the orientation of the line relative to that vector so a third point might be preferable if you have it.
Construct an orthonormalized 3x3 rotation matrix:
Use the Grahm-Schmit method to orthonormalize the first two rows where:
u1 = p2 - p1
u2 = p3 - p1 (or a constant vector)
After applying Grahm-Schmit, these vectors will become the first 2 rows of your 3x3 matrix.
The third row of your matrix is just the cross-product of those first two rows.
Decompose the resulting matrix into euler angles.