Creating a view matrix with glm - c++

I am trying to create a view matrix with glm. I know of glm::lookAt and understand how it works. I want to know if there are similar functions that will acheive the same outcome that take different parameters. For example, is there a function that takes an up-vector, a directional bearing on the plane perpendicular(?) to the vector, and an angle? (i.e. The sky is that way, I turn N degrees/radians to my left and tilt my head M degrees/radians upward).

You can just build the matrix by composing a set of operations:
// define your up vector
glm::vec3 upVector = glm::vec3(0, 1, 0);
// rotate around to a given bearing: yaw
glm::mat4 camera = glm::rotate(glm::mat4(), bearing, upVector);
// Define the 'look up' axis, should be orthogonal to the up axis
glm::vec3 pitchVector = glm::vec3(1, 0, 0);
// rotate around to the required head tilt: pitch
camera = glm::rotate(camera, tilt, pitchVector);
// now get the view matrix by taking the camera inverse
glm::mat4 view = glm::inverse(camera);

Related

Is there any way to extract a transform matrix from a view matrix in glm?

I need to extract the transform matrix from my camera to assign it to a mesh.
I'm working in a computational graphics project in school, the objective is to simulate the arms of a character in first person perspective.
My camera implementation includes a vector3 for the camera position, so i can assign that to my mesh, the problem is that i can't extract the rotation of the camera from my view matrix yet.
I calculate my final pitch and yaw in the rotation function this way, x and y are the current mouse position in the screen
m_yaw += (x - m_mouseLastPosition.x) * m_rotateSpeed;
m_pitch -= (y - m_mouseLastPosition.y) * m_rotateSpeed;
This is how i update the view matrix when it changes
glm::vec3 newFront;
newFront.x = -cos(glm::radians(m_yaw)) * cos(glm::radians(m_pitch));
newFront.y = sin(glm::radians(m_yaw)) * cos(glm::radians(m_pitch));
newFront.z = sin(glm::radians(m_pitch));
m_front = glm::normalize(newFront);
m_right = glm::normalize(glm::cross(m_front, m_worldUp));
m_up = glm::normalize(glm::cross(m_right, m_front));
m_viewMatrix = glm::lookAt(m_position, (m_position + m_front), m_up);
Right now I can assign the position of the camera to my mesh, like this
m_mesh.m_transform = glm::translate(glm::mat4(1.0f), m_camera.m_position);
I can assign the camera position successfully, but not rotation.
What i expect is to assign the full camera transform to my mesh, or to extract the rotation independently and assign it to the mesh after.
The steps to setting up the model view projection matrix, that I have always followed (which doesn't mean it's 100% right), which appears to be what you are having problems with is:
// Eye position is in world coordinate system, as is scene_center. up_vector is normalized.
glm::dmat4 view = glm::lookat(eye_position, scene_center, up_vector);
glm::dmat4 proj = glm::perspective(field_of_view, aspect, near_x, far_x);
// This converts the model from it's units, to the units of the world coordinate system
glm::dmat4 model = glm::scale(glm::dmat4(1.0), glm::dvec3(1.0, 1.0, 1.0));
// Add model level rotations here utilizing glm::rotate
// offset is where the objects 0,0 should be mapped to in the world coordinate system
model = glm::translate(model, offset);
// Order of course matters here.
glm::dvec3 mvp = proj * view * model;
Hope that helps.
Thank you so much for your answers, they helped a lot.
I managed to solve my problem in a very simple way, i just had to directly assign the final transform to my mesh using the separate properties of my camera.
glm is so new to me, i wasn't familiar with the way it handles matrix multiplications.
The final code takes a translation matrix with the camera position, then i rotated the resulting matrix in the Y axis with my pitch and finaly the resulting one rotates in the X axis with my yaw.
m_mesh.m_transform = glm::rotate(glm::rotate(glm::translate(glm::mat4(1.0f), camera.m_position), glm::radians(-camera.m_yaw), glm::vec3(0.0f, 1.0f, 0.0f)), glm::radians(-camera.m_pitch), glm::vec3(1.0f, 0.0f, 0.0f));

object keep his distance around camera c++ opengl

I want to keep the object permanently at a certain distance from the camera. How i can made this? I tried this:
vec3 obj_pos = -cam->Get_CameraPos() ;
obj_pos .z -= 10.0f ;
...
o_modelMatrix = glm::translate(o_modelMatrix, obj_pos);
but it's not working; The object simply stands on the determined position and not moving
Full code of render:
void MasterRenderer::renderPlane() {
PlaneShader->useShaderProgram();
glm::mat4 o_modelMatrix;
glm::mat4 o_view = cam->Get_ViewMatrix();
glm::mat4 o_projection = glm::perspective(static_cast<GLfloat>(glm::radians(cam->Get_fov())),
static_cast<GLfloat>(WIDTH) / static_cast<GLfloat>(HEIGHT), 0.1f, 1000.0f);
glUniformMatrix4fv(glGetUniformLocation(PlaneShader->ShaderProgramID, "projection"), 1, GL_FALSE, glm::value_ptr(o_projection ));
glUniformMatrix4fv(glGetUniformLocation(PlaneShader->ShaderProgramID, "view"), 1, GL_FALSE, glm::value_ptr(o_view ));
vec3 eye_pos = vec3(o_view [3][0], o_view [3][1], o_view [3][2]); //or cam->getCameraPosition();
glm::vec3 losDirection = glm::normalize(vec3(0.0f, 0.0f, -1.0f) - eye_pos);
vec3 obj_pos = eye_pos + losDirection * 1.0f;
b_modelMatrix = scale(o_modelMatrix, vec3(20.0f));
b_modelMatrix = glm::translate(b_modelMatrix, obj_pos );
glUniformMatrix4fv(glGetUniformLocation(PlaneShader->ShaderProgramID,
"model"), 1, GL_FALSE, glm::value_ptr(o_modelMatrix));
...
/// draw
Maybe this is a shot from the hip, but I suppose that you set up a lookat matrix and that you the position of your object is defined in world coordinates.
Commonly a camera is defined by a eye position, at target (center) position and an up vector.
The direction in which the camera looks is the line of sight, which is the unit vector from the eye position to the target position.
Calcualte the line of sight:
glm::vec3 cameraPosition ...; // the eye position
glm::vec3 cameraTarget ...; // the traget (center) posiiton
glm::vec3 losDirection = glm::normalize( cameraTarget - cameraPosition );
Possibly the camera class knows the direction of view (line of sight), then you can skip this calculation.
If the object is always to be placed a certain distance in front of the camera, the position of the object is the position of the camera plus a distance in the direction of the line of sight:
float distance = ...;
float objectPosition = cameraPosition + losDirection * distance;
glm::mat4 modelPosMat = glm::translate( glm::mat4(1.0f) , objectPosition );
glm::mat4 objectModelMat = ...; // initial model matrix of the object
o_modelMatrix = modelPosMat * objectModelMat;
Note the objectModelMat is the identity matrix if the object has no further transformations glm::mat4(1.0f).
so you want to move the object with camera (instead of moving camera with object like camera follow). If this is just for some GUI stuff you can use different static view matrices for it. But if you want to do this in way you suggested then this is the way:
definitions
First we need few 3D 4x4 homogenuous transform matrices (read the link to see how to disect/construct what you need). So lets define some matrices we need for this:
C - inverse camera matrix (no projection)
M - direct object matrix
R - direct object rotation
Each matrix has 4 vectors X,Y,Z are the axises of the coordinate system represented by it and O is the origin. Direct matrix means the matrix directly represents the coordinate system and inverse means that it is the inverse of such matrix.
Math
so we want to construct M so it is placed at some distance d directly in front of C and has rotation R. I assume you are using perspective projection and C viewing direction is -Z axis. So what you need to do is compute position of M. That is easy you just do this:
iC = inverse(C); // get the direct matrix of camera
M = R; // set rotation of object
M.O = iC.O - d*iC.Z; // set position of object
The M.O = (M[12],M[13],M[14]) and iC.Z = (iC.Z[8],iC.Z[9],iC.Z[10]) so if you got direct access to your matrix you can do this on your own in case GLM does not provide element access.
Beware that all this is for standard OpenGL matrix convention and multiplication order. If you use DirectX convention instead then M,R are inverse and C is direct matrix so you would need to change the equations accordingly. Sorry I do not use GLM so I am not confident to generate any code for you.
In case you want to apply camera rotations on object rotations too then you need to change M = R to M = R*iC or M = iC*R which depends on what effect you want to achieve.
It's work fine with not multiplication, but addition
obj_pos = glm::normalize(glm::cross(vec3(0.0f, 0.0f, -1.0f), vec3(0.0f, 1.0f, 0.0f)));
o_modelMatrix[3][0] = camera_pos.x;
o_modelMatrix[3][1] = camera_pos.y;
o_modelMatrix[3][2] = camera_pos.z + distance;
o_modelMatrix = glm::translate(o_modelMatrix, obj_pos);

Converting glm::lookat matrix to quaternion and back

I am using glm to create a camera class, and I am running into some problems with a lookat function. I am using a quaternion to represent rotation, but I want to use glm's prewritten lookat function to avoid duplicating code. This is my lookat function right now:
void Camera::LookAt(float x, float y, float z) {
glm::mat4 lookMat = glm::lookAt(position, glm::vec3(x, y, z), glm::vec3(0, 1, 0));
rotation = glm::toQuat(lookMat);
}
However when I call LookAt(0.0f,0.0f,0.0f), my camera is not rotated to that point. When I call glm::eulerangles(rotation) after the lookat call, I get a vec3 with the following values: (180.0f, 0.0f, 180.0f). position is (0.0f,0.0f,-10.0f), so I should not have any rotation at all to look at 0,0,0. This is the function which builds the view matrix:
glm::mat4 Camera::GetView() {
view = glm::toMat4(rotation) * glm::translate(glm::mat4(), position);
return view;
}
Why am I not getting the correct quaternion, and how can I fix my code?
Solution:
You have to invert the rotation of the quaternion by conjugating it:
using namespace glm;
quat orientation = conjugate(toQuat(lookAt(vecA, vecB, up)));
Explanation:
The lookAt function is a replacement for gluLookAt, which is used to construct a view matrix.
The view matrix is used to rotate the world around the viewer, and is therefore the inverse of the cameras transform.
By taking the inverse of the inverse, you can get the actual transform.
I ran into something similar, the short answer is your lookMat might need to be inverted/transposed, because it is a camera rotation (at least in my case), as opposed to a world rotation. Rotating the world would be a inverse of a camera rotation.
I have a m_current_quat which is a quaternion that stores the current camera rotation. I debugged the issue by printing out the matrix produced by glm::lookAt, and comparing with the resulting matrix that I get by applying m_current_quat and a translation by m_camera_position. Here is the relevant code for my test.
void PrintMatrix(const GLfloat m[16], const string &str)
{
printf("%s:\n", str.c_str());
for (int i=0; i<4; i++)
{
printf("[");
//for (int j=i*4+0; j<i*4+4; j++) // row major, 0, 1, 2, 3
for (int j=i+0; j<16; j+=4) // OpenGL is column major by default, 0, 4, 8, 12
{
//printf("%d, ", j); // print matrix index
printf("%.2f, ", m[j]);
}
printf("]\n");
}
printf("\n");
}
void CameraQuaternion::SetLookAt(glm::vec3 look_at)
{
m_camera_look_at = look_at;
// update the initial camera direction and up
//m_initial_camera_direction = glm::normalize(m_camera_look_at - m_camera_position);
//glm::vec3 initial_right_vector = glm::cross(m_initial_camera_direction, glm::vec3(0, 1, 0));
//m_initial_camera_up = glm::cross(initial_right_vector, m_initial_camera_direction);
m_camera_direction = glm::normalize(m_camera_look_at - m_camera_position);
glm::vec3 right_vector = glm::cross(m_camera_direction, glm::vec3(0, 1, 0));
m_camera_up = glm::cross(right_vector, m_camera_direction);
glm::mat4 lookat_matrix = glm::lookAt(m_camera_position, m_camera_look_at, m_camera_up);
// Note: m_current_quat quat stores the camera rotation with respect to the camera space
// The lookat_matrix produces a transformation for world space, where we rotate the world
// with the camera at the origin
// Our m_current_quat need to be an inverse, which is accompolished by transposing the lookat_matrix
// since the rotation matrix is orthonormal.
m_current_quat = glm::toQuat(glm::transpose(lookat_matrix));
// Testing: Make sure our model view matrix after gluLookAt, glmLookAt, and m_current_quat agrees
GLfloat current_model_view_matrix[16];
//Test 1: gluLookAt
gluLookAt(m_camera_position.x, m_camera_position.y, m_camera_position.z,
m_camera_look_at.x, m_camera_look_at.y, m_camera_look_at.z,
m_camera_up.x, m_camera_up.y, m_camera_up.z);
glGetFloatv(GL_MODELVIEW_MATRIX, current_model_view_matrix);
PrintMatrix(current_model_view_matrix, "Model view after gluLookAt");
//Test 2: glm::lookAt
lookat_matrix = glm::lookAt(m_camera_position, m_camera_look_at, m_camera_up);
PrintMatrix(glm::value_ptr(lookat_matrix), "Model view after glm::lookAt");
//Test 3: m_current_quat
glLoadIdentity();
glMultMatrixf( glm::value_ptr( glm::transpose(glm::mat4_cast(m_current_quat))) );
glTranslatef(-m_camera_position.x, -m_camera_position.y, -m_camera_position.z);
glGetFloatv(GL_MODELVIEW_MATRIX, current_model_view_matrix);
PrintMatrix(current_model_view_matrix, "Model view after quaternion transform");
return;
}
Hope this helps.
I want to use glm's prewritten lookat function to avoid duplicating code.
But it's not duplicating code. The matrix that comes out of glm::lookat is just a mat4. Going through the conversion from a quaternion to 3 vectors, only so that glm::lookat can convert it back into an orientation is just a waste of time. You've already done 85% of lookat's job; just do the rest.
You are getting the (or better: a) correct rotation.
When I call glm::eulerangles(rotation) after the lookat call, I get a
vec3 with the following values: (180.0f, 0.0f, 180.0f). position is
(0.0f,0.0f,-10.0f), so I should not have any rotation at all to look
at 0,0,0.
glm is following the conventions of the old fixed-function GL. And there, eye space was defined as the camera placed at origin, with x pointng to the right, y up and looking in -z direction. Since you want to look in positive z direction, the camera has to turn. Now, as a human, I would have described that as a rotation of 180 degrees around y, but a rotation of 180 degrees around x in combination with another 180 degrees rotation aroundz will have the same effect.
When multiplied by the LookAt view matrix, the world-space vectors are rotated (brought) into the camera's view while the camera's orientation is kept in place.
So an actual rotation of the camera by 45 degress to the right is achieved with a matrix which applies a 45 degree rotation to the left to all the world-space vertices.
For a Camera object you would need to get its local forward and up direction vectors in order to calculate a lookAt view matrix.
viewMatrix = glm::lookAtLH (position, position + camera_forward, camera_up);
When using quaternions to store the orientation of an object (be it a camera or anything else), usually this rotation quat is used to calculate the vectors which define its local-space (left-handed one in the below example):
glm::vec3 camera_forward = rotation * glm::vec3(0,0,1); // +Z is forward direction
glm::vec3 camera_right = rotation * glm::vec3(1,0,0); // +X is right direction
glm::vec3 camera_up = rotation * glm::vec3(0,1,0); // +Y is up direction
Thus, the world-space directions should be rotated 45 degress to the right in order to reflect the correct orientation of the camera.
This is why the lookMat or the quat obtained from it cannot be directly used for this purpose, since the orientation they describe is a reversed one.
Correct rotation can be done in two ways:
Calculate the inverse of the lookAt matrix and multiply the world-space direction vectors by this rotation matrix
(more efficient) Convert the LookAt matrix into a quaternion and conjugate it instead of applying glm::inverse, since the result is a unit quat and for such quats the inverse is equal to the conjugate.
Your LookAt should look like this:
void Camera::LookAt(float x, float y, float z) {
glm::mat4 lookMat = glm::lookAt(position, glm::vec3(x, y, z), glm::vec3(0, 1, 0));
rotation = glm::conjugate( glm::quat_cast(lookMat));
}

Issue with GLM Camera X,Y Rotation introducing Z Rotation

So I've been having trouble with a camera I've implemented in OpenGL and C++ using the GLM library. The type of camera I'm aiming for is a fly around camera which will allow easy exploration of a 3D world. I have managed to get the camera pretty much working, it's nice and smooth, looks around and the movement seems to be nice and correct.
The only problem I seem to have is that the rotation along the camera's X and Y axis (looking up and down) introduces some rotation about it's Z axis. This has the result of causing the world to slightly roll whilst travelling about.
As an example... if I have a square quad in front of the camera and move the camera in a circular motion, so as if looking around in a circle with your head, once the motion is complete the quad will have rolled slightly as if you've tilted your head.
My camera is currently a component which I can attach to an object/entity in my scene. Each entity has a "Frame" which is basically the model matrix for that entity. The Frame contains the following attributes:
glm::mat4 m_Matrix;
glm::vec3 m_Position;
glm::vec3 m_Up;
glm::vec3 m_Forward;
These are then used by the camera to create the appropriate viewMatrix like this:
const glm::mat4& CameraComponent::GetViewMatrix()
{
//Get the transform of the object
const Frame& transform = GetOwnerGO()->GetTransform();
//Update the viewMatrix
m_ViewMatrix = glm::lookAt(transform.GetPosition(), //position of camera
transform.GetPosition() + transform.GetForward(), //position to look at
transform.GetUp()); //up vector
//return reference to the view matrix
return m_ViewMatrix;
}
And now... here are my rotate X and Y methods within the Frame object, which I'm guessing is the place of the problem:
void Frame::RotateX( float delta )
{
glm::vec3 cross = glm::normalize(glm::cross(m_Up, m_Forward)); //calculate x axis
glm::mat4 Rotation = glm::rotate(glm::mat4(1.0f), delta, cross);
m_Forward = glm::normalize(glm::vec3(Rotation * glm::vec4(m_Forward, 0.0f))); //Rotate forward vector by new rotation
m_Up = glm::normalize(glm::vec3(Rotation * glm::vec4(m_Up, 0.0f))); //Rotate up vector by new rotation
}
void Frame::RotateY( float delta )
{
glm::mat4 Rotation = glm::rotate(glm::mat4(1.0f), delta, m_Up);
//Rotate forward vector by new rotation
m_Forward = glm::normalize(glm::vec3(Rotation * glm::vec4(m_Forward, 0.0f)));
}
So somewhere in there, there's a problem which I've been searching around trying to fix. I've been messing with it for a few days now, trying random things but I either get the same result, or the z axis rotation is fixed but other bugs appear such as incorrect X, Y rotation and camera movement.
I had a look at gimbal lock but from what I understood of it, this problem didn't seem quite like gimbal lock to me. But I may be wrong.
Store the current pitch/yaw angles and generate the camera matrix on-the-fly instead of trying to accumulate small changes on the intermediate vectors.
In your RotateY function, change it from this:
glm::mat4 Rotation = glm::rotate(glm::mat4(1.0f), delta, m_Up);
to this:
glm::mat4 Rotation = glm::rotate(glm::mat4(1.0f), delta, glm::vec3(0,1,0));

OpenGL simultaneous translate and rotate around local axis

I am working on an application that has similar functionality to MotionBuilder in its viewport interactions. It has three buttons:
Button 1 rotates the viewport around X and Y depending on X/Y mouse drags.
Button 2 translates the viewport around X and Y depending on X/Y mouse drags.
Button 3 "zooms" the viewport by translating along Z.
The code is simple:
glTranslatef(posX,posY,posZ);
glRotatef(rotX, 1, 0, 0);
glRotatef(rotY, 0, 1, 0);
Now, the problem is that if I translate first, the translation will be correct but the rotation then follows the world axis. I've also tried rotating first:
glRotatef(rotX, 1, 0, 0);
glRotatef(rotY, 0, 1, 0);
glTranslatef(posX,posY,posZ);
^ the rotation works, but the translation works according to world axis.
My question is, how can I do both so I achieve the translation from code snippet one and the rotation from code snippet 2.
EDIT
I drew this rather crude image to illustrate what I mean by world and local rotations/translations. I need the camera to rotate and translate around its local axis.
http://i45.tinypic.com/2lnu3rs.jpg
Ok, the image makes things a bit clearer.
If you were just talking about an object, then your first code snippet would be fine, but for the camera it's quite different.
Since there's technically no object as a 'camera' in opengl, what you're doing when building a camera is just moving everything by the inverse of how you're moving the camera. I.e. you don't move the camera up by +1 on the Y axis, you just move the world by -1 on the y axis, which achieves the same visual effect of having a camera.
Imagine you have a camera at position (Cx, Cy, Cz), and it has x/y rotation angles (CRx, CRy). If this were just a regular object, and not the camera, you would transform this by:
glTranslate(Cx, Cy, Cz);
glRotate(CRx, 1, 0, 0);
glRotate(CRy, 0, 1, 0);
But because this is the camera, we need to do the inverse of this operation instead (we just want to move the world by (-Cx, -Cy, and -Cz) to emulate the moving of a 'camera'. To invert the matrix, you just have to do the opposite of each individual transform, and do them in reverse order.
glRotate(-CRy, 0, 1, 0);
glRotate(-CRx, 1, 0, 0);
glTranslate(-Cx, -Cy, -Cz);
I think this will give you the kind of camera you're mentioning in your image.
I suggest that you bite the apple and implement a camera class that stores the current state of the camera (position, view direction, up vector, right vector) and manipulate that state according to your control scheme. Then you can set up the projection matrix using gluLookAt(). Then, the order of operations becomes unimportant. Here is an example:
Let camPos be the current position of the camera, camView its view direction, camUp the up vector and camRight the right vector.
To translate the camera by moveDelta, simply add moveDelta to camPos. Rotation is a bit more difficult, but if you understand quaternions you'll be able to understand it quickly.
First you need to create a quaternion for each of your two rotations. I assume that your horizontal rotation is always about the positive Z axis (which points at the "ceiling" if you will). Let hQuat be the quaternion representing the horizontal rotation. I further assume that you want to rotate the camera about its right axis for your vertical rotation (creating a pitch effect). For this, you must apply the horizontal rotation to the camera's current angle. The result is the rotation axis for your vertical rotation hQuat. The total rotation quaternion is then rQuat = hQuat * vQuat. Then you apply rQuat to the camera's view direction, up, and right vectors.
Quat hRot(rotX, 0, 0, 1); // creates a quaternion that rotates by angle rotX about the positive Z axis
Vec3f vAxis = hRot * camRight; // applies hRot to the camera's right vector
Quat vRot(rotY, vAxis); // creates a quaternion that rotates by angle rotY about the rotated camera's right vector
Quat rQuat = hRot * vRot; // creates the total rotation
camUp = rQuat * camUp;
camRight = rQuat * camRight;
camView = rQuat * camView;
Hope this helps you solve your problem.
glRotate always works around the origin. If you do:
glPushMatrix();
glTranslated(x,y,z);
glRotated(theta,1,0,0);
glTranslated(-x,-y,-z);
drawObject();
glPopMatrix();
Then the 'object' is rotate around (x,y,z) instead of the origin, because you moved (x,y,z) to the origin, did the rotation, and then pushed (x,y,z) back where it started.
However, I don't think that's going to be enough to get the effect you're describing. If you always want transformations to be done with respect to the current frame of reference, then you need to keep track of the transformation matrix yourself. This why people use Quaternion based cameras.