Simply put, I'm learning OpenGL and am starting to learn about transform matrices. Below is my current code:
glm::vec4 myPosition(1.0f, 1.0f, 1.0f, 1.0f);
glm::vec3 rotationAxis(0.0f, 1.0f, 0.0f);
glm::mat4 scalar = glm::scale(glm::vec3(1.0f, 1.0f, 1.0f));
glm::mat4 rotator = glm::rotate(360.0f, rotationAxis);
glm::mat4 translator = glm::translate(glm::vec3(1.0f, 1.0f, 1.0f));
glm::mat4 transform = translator * rotator * scalar;
myPosition = transform * myPosition;
As far as I can tell, I'm doing this in the correct order: Scale -> Rotate -> Translate. So, I have the scale set to do nothing because I don't actually want it to scale anywhere (for simplicity sake).
Next, I set rotate to 360.0f on (correct me if I'm wrong) the Y axis. This should return to the original point, at least that's what I would think from a 360 degree rotation around a singular axis.
Then, I set it to translate 1 unit in every direction to make sure it moves.
After finishing this all, I have commented out the rotator line, and it works fantastic, even if I change the scale. However, whenever I add in the rotate line the final position is not a normal 360 degree rotation?
I have configured my program to output the position vector both before transforms and after.
The before position is (1, 1, 1)
The after position is (1.67522, 2, -0.242607).
I have been struggling to find my error, literally all day so if anyone can help me find what I'm doing wrong, it would be greatly appreciated!!
According to the documentation at http://glm.g-truc.net/0.9.7/api/a00236.html (for the latest released version right now), glm::rotate(..) takes in an angle expressed in degrees.
However, changing your rotation matrix line
glm::mat4 rotator = glm::rotate(360.0f, rotationAxis);
to
glm::mat4 rotator = glm::rotate(3.141592f * 2.0f, rotationAxis);
which is just 2*PI fixes this.
This means that the angle should be in radians rather than in degrees. Tested on my machine with GLM 0.9.7.1-1. This is either a mistake in the documentation or in the GLM code itself.
From my experience with GLM (some time ago, might've been an earlier version) these kinds of functions should take a degrees angle by default, and the #define GLM_FORCE_RADIANS macro is what makes them calculate in radians. It is possible that the author made this the default behaviour and forgot to update the docs.
On a side note, you should probably not be using scalar as the name for a glm::mat4 value, since a scalar in mathematics is just a single real number rather than a matrix: https://en.wikipedia.org/wiki/Scalar_%28mathematics%29
Related
I am trying to make my world rotate around my camera no matter where my camera is. I am not doing any crazy math yet, I am leaving middle school this year and I don't know what quaternions are. My problem is that every time I use glm::rotate function for anything it only allows me to rotate around an axis at the origin point and I can't find a way to fix this. if there is any somewhat simple answer to this problem I am having please let me know how I can rotate my world around any given point. thanks
glm::mat4 look(1.0f);
float Rrotation;
Rrotation = 20.0f;
glm::vec3 the_axis_not_orientation(0.0f, 1.0f, 0.0f);
look = glm::rotate(look, Rrotation, the_axis_not_orientation);
What you actually do is to rotate the model:
model_view = look * rotate
If you want to rotate the view, then you have to swap the order of the matrices. Note, the matrix multiplication is not Commutative:
model_view = rotate * look
For your code that menas:
glm::mat4 rotate = glm::rotate(glm::mat4(1.0f), Rrotation, the_axis_not_orientation)
look = rotate * look;
I am trying to implement a camera which orbits around the origin, where I have successfully implemented the ability to yaw using the gluLookat function. I am trying to implement pitch, but have a few issues with the outcome (pitch only works if I yaw to a certain point and then pitch).
Here is my attempt so far:
float distance, // radius (from origin) updated by -, + keys
pitch, // angle in degrees updated from W, S keys (increments of +- 10)
yaw; // angle in degrees updated from A, D keys (increments of +- 10)
view = lookAt(
Eigen::Vector3f(distance * sin(toRadians(pitch)) * cos(toRadians(yaw)), distance * sin(toRadians(pitch)) * sin(toRadians(yaw)), distance * cos(toRadians(pitch))),
Eigen::Vector3f(0.0f, 0.0f, 0.0f),
Eigen::Vector3f(0.0f, 0.0f, 1.0f));
proj = perspective(toRadians(90.0f), static_cast<float>(width) / height, 1.0f, 10.0f);
I feel like my issue is the Up vector, but I'm not sure how to update it properly(and at the same time I think its fine, as I always want the orientation of the camera to stay the same, I really just want to move the position of the camera)
Edit: I wanted to add that I'm calculating the position based info found here: http://tutorial.math.lamar.edu/Classes/CalcIII/SphericalCoords.aspx I'm not sure if the math discussed here directly translates over so please correct me if wrong.
It might be a matter of interpretation. Your code looks correct but pitch might not have the meaning that you think.
When pitch is 0, the camera is located at the north pole of the sphere (0, 0, 1). This is a bit problematic since your up-vector and view direction become parallel and you will not get a valid transform. Then, when pitch increases, the camera moves south until it reaches the south pole when pitch=PI. Your code should work for any point that is not at the poles. You might want to swap sin(pitch) and cos(pitch) to start at the equator when pitch=0 (and support positive and negative pitch).
Actually, I prefer to model this kind of camera more directly as a combination of matrices:
view = Tr(0, 0, -distance) * RotX(-pitch) * RotY(-yaw)
Tr is a translation matrix, RotX is a rotation about the x-axis, and RotY is a rotation about the y-axis. This assumes that the y-axis is up. If you want another axis to be up, you can just add an according rotation matrix. E.g., if you want the z-axis to be up, then
view = Tr(0, 0, -distance) * RotX(-pitch) * RotY(-yaw) * RotX(-Pi/2)
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.
I have looked at a ton of quaternion examples on several sites including this one, but found none that answer this, so here goes...
I want to orbit my camera around a large sphere, and have the camera always point at the center of the sphere. To make things easy, the sphere is located at {0,0,0} in the world. I am using a quaternion for camera orientation, and a vector for camera position. The problem is that the camera position orbits the sphere perfectly as it should, but always looks one constant direction straight forward instead of adjusting to point to the center as it orbits.
This must be something simple, but I am new to quaternions... what am I missing?
I'm using C++, DirectX9. Here is my code:
// Note: g_camRotAngleYawDir and g_camRotAnglePitchDir are set to either 1.0f, -1.0f according to keypresses, otherwise equal 0.0f
// Also, g_camOrientationQuat is just an identity quat to begin with.
float theta = 0.05f;
D3DXQUATERNION g_deltaQuat( 0.0f, 0.0f, 0.0f, 1.0f );
D3DXQuaternionRotationYawPitchRoll(&g_deltaQuat, g_camRotAngleYawDir * theta, g_camRotAnglePitchDir * theta, g_camRotAngleRollDir * theta);
D3DXQUATERNION tempQuat = g_camOrientationQuat;
D3DXQuaternionMultiply(&g_camOrientationQuat, &tempQuat, &g_deltaQuat);
D3DXMatrixRotationQuaternion(&g_viewMatrix, &g_camOrientationQuat);
g_viewMatrix._41 = g_camPosition.x;
g_viewMatrix._42 = g_camPosition.y;
g_viewMatrix._43 = g_camPosition.z;
g_direct3DDevice9->SetTransform( D3DTS_VIEW, &g_viewMatrix );
[EDIT - Feb 13, 2012]
Ok, here's my understanding so far:
move the camera using an angular delta each frame.
Get a vector from center to camera-pos.
Call quaternionRotationBetweenVectors with a z-facing unit vector and the target vector.
Then use the result of that function for the orientation of the view matrix, and the camera-position goes in the translation portion of the view matrix.
Here's the new code (called every frame)...
// This part orbits the position around the sphere according to deltas for yaw, pitch, roll
D3DXQuaternionRotationYawPitchRoll(&deltaQuat, yawDelta, pitchDelta, rollDelta);
D3DXMatrixRotationQuaternion(&mat1, &deltaQuat);
D3DXVec3Transform(&g_camPosition, &g_camPosition, &mat1);
// This part adjusts the orientation of the camera to point at the center of the sphere
dir1 = normalize(vec3(0.0f, 0.0f, 0.0f) - g_camPosition);
QuaternionRotationBetweenVectors(&g_camOrientationQuat, vec3(0.0f, 0.0f, 1.0f), &dir1);
D3DXMatrixRotationQuaternion(&g_viewMatrix, &g_camOrientationQuat);
g_viewMatrix._41 = g_camPosition.x;
g_viewMatrix._42 = g_camPosition.y;
g_viewMatrix._43 = g_camPosition.z;
g_direct3DDevice9->SetTransform( D3DTS_VIEW, &g_viewMatrix );
...I tried that solution out, without success. What am I doing wrong?
It should be as easy as doing the following whenever you update the position (assuming the camera is pointing along the z-axis):
direction = normalize(center - position)
orientation = quaternionRotationBetweenVectors(vec3(0,0,1), direction)
It is fairly easy to find examples of how to implement quaternionRotationBetweenVectors. You could start with the question "Finding quaternion representing the rotation from one vector to another".
Here's an untested sketch of an implementation using the DirectX9 API:
D3DXQUATERNION* quaternionRotationBetweenVectors(__inout D3DXQUATERNION* result, __in const D3DXVECTOR3* v1, __in const D3DXVECTOR3* v2)
{
D3DXVECTOR3 axis;
D3DXVec3Cross(&axis, v1, v2);
D3DXVec3Normalize(&axis, &axis);
float angle = acos(D3DXVec3Dot(v1, v2));
D3DXQuaternionRotationAxis(result, &axis, angle);
return result;
}
This implementation expects v1, v2 to be normalized.
You need to show how you calculate the angles. Is there a reason why you aren't using D3DXMatrixLookAtLH?
I just don't seem to be able to figure this out in my head. I'm trying to move an object in 3D space.
If I have a point at 5,15,5 and use opengl functions to change the model view....
glTranslatef( 10.0f, 4.0f, 4.0f );
glRotatef( 33.0f, 1.0f, 0.0f, 0.0f );
glTranslatef( 10.0f, 4.0f, 4.0f );
Is there a way I can find out where that point ends up (in world / global coordinates)?
Can I do some kind of matrix calculations that will give me back 20,26,23 (or what every the new coordinate position is)?
Please help, I've been stuck on this for so long!
Try the following:
1) Push the current matrix into stack;
2) Load identity and apply your transformations;
3) Get the resulting transformation matrix into some temp variable. glGet or something like that will help;
4) Pop the matrix from the stack;
Now you have your transformation matrix. Multiply your point by this matrix to predict the point's coordinates after the transformation.
Definitely: check out http://research.cs.queensu.ca/~jstewart/454/notes/pipeline/
In short, all of these calls reduce to a single matrix, which is multiplied onto the point.
SadSido's method will definitely get you the resultant matrix, but it may not hurt to actually understand what's going on behind the scenes. The calculations above will result in a linear algebra equation of the following:
pOut = [mTranslate] * [mRotate] * [mTranslate] * pIn
where mTranslate = the translation calls (matrix for translation), and mRotate = rotate call (matrix for rotation about an arbitrary axis). Calculate that, and you're good to go!