IMPORTANT TO NOTE that I am using Row Matrix order. so multiplication of matrices happens as its written (left to right). So Make sure that you transpose your matrix before you multiplying with Model Matrices.
I hated the fact that the GLuLookAt() always limited me to level where I couldn't have full control of my camera. For this reason I implemented my own camera classes. I am going to assume that you want a third person camera that can do rotation about x (pitch) and y(yaw) axis.
Before going any further, let me remind you what is a model view matrix is. In OpenGL no such thing as camera matrix. its assumed as a matrix that lies at the origin with no rotation applied to it. In terms of 3D world, Model view matrix is a multiplication of a model matrix with the view matrix. hence unsurprisingly its been named as model-view matrix. Model matrix is as you may recall is the matrix that transforms the object into world space. Let me rephrase the fact that Model-view matrix in OpenGL is actually just Model matrix. However, its much more sensible to imagine this Model-View matrix as a product of Model matrix times the Identity matrix. Identity matrix does no transformation on the model matrix. We can think this identity matrix as a placement matrix to the camera matrix we going to create. I am going to go with full implementation first, then go through step by step explaining on whats happening.
Here comes the rather long code.
void ThirdPersonCamera::CalculateViewMatrix(mat3 modelRotation, vec3 modelPos)
{
mat4 cameraRotationMatrix; //holds the rotation of the camera.
mat3 invModelrotation; //inverted rotation matrix for models rotation
mat4 invModelTranslation; //inverted translation matrix for model's translation.
if(m_f_pitch_angle > 0.4f)
m_f_pitch_angle = 0.4f;
else if(m_f_pitch_angle < -0.4f)
m_f_pitch_angle = -0.4f;
if(m_f_yaw_angle > TwoPi) m_f_yaw_angle = -TwoPi;
else if(m_f_yaw_angle < -TwoPi) m_f_yaw_angle += TwoPi;
m_fv3_right = fvec3(1.0f,0.0f,0.0f);
m_fv3_up = fvec3(0.0f, 1.0f, 0.0f);
m_fv3_direction = fvec3(0.0f,0.0f, 1.0f);
mat3 mat; //will hold arbitrary rotation temporarily
//rotate arbitrarily about the right base vector(x axis)
mat = mat.RotateArbitrary(m_f_pitch_angle, m_fv3_right);
m_fv3_up = m_fv3_up * mat;
m_fv3_direction = m_fv3_direction * mat;
//rotate arbitrarily up base vector(y axis)
mat = mat.RotateArbitrary(m_f_yaw_angle, m_fv3_up);
m_fv3_right = mat * m_fv3_right;
m_fv3_direction = mat * m_fv3_direction;
//now we can create our base vectors for camera rotation matrix.
cameraRotationMatrix.x.x = m_fv3_right.x;
cameraRotationMatrix.x.y = m_fv3_right.y;
cameraRotationMatrix.x.z = m_fv3_right.z;
cameraRotationMatrix.y.x = m_fv3_up.x;
cameraRotationMatrix.y.y = m_fv3_up.y;
cameraRotationMatrix.y.z = m_fv3_up.z;
cameraRotationMatrix.z.x = m_fv3_direction.x;
cameraRotationMatrix.z.y = m_fv3_direction.y;
cameraRotationMatrix.z.z = m_fv3_direction.z;
//create local translation matrix that translates the camera at the given radius away from player
mat4 locTranslation = mat4::Translate(m_fv3_pos);
//create local rotation matrix that rotates the camera at the back of the player.
mat4 localRot = localRot.yRotate(180);
//we need to transform our view matrix into the object space of the model. To do this we need to get its inverse matrix
invModelrotation.x.x = modelRotation->x.x;
invModelrotation.x.y = modelRotation->x.y;
invModelrotation.x.z = modelRotation->x.z;
invModelrotation.y.x = modelRotation->y.x;
invModelrotation.y.y = modelRotation->y.y;
invModelrotation.y.z = modelRotation->y.z;
invModelrotation.z.x = modelRotation->z.x;
invModelrotation.z.y = modelRotation->z.y;
invModelrotation.z.z = modelRotation->z.z;
invModelrotation.Transpose();//inverse the rotation it should work since this is a matrix with orthogonal bases.
//inverse the translation
invModelTranslation.w.x = -modelPosition->x;
invModelTranslation.w.y = -modelPosition->y;
invModelTranslation.w.z = -modelPosition->z;
//now we can build our view matrix
m_viewMatrix = (invModelTranslation * invModelrotation) * (cameraRotationMatrix * (locTranslation * localRot));
}
Ok. Lets go step by step on whats going on here. first things that I do is making sure the pitch and yaw angles doesn't exceed a range. in this case I limit the pitch angle to 23 to -23 angle.this is the amount of rotation can be done about the x axis by user. Also made sure the yaw angle are in the range of 360 to -360 degree.
In addition to above, I made sure that my up, right and direction vectors are unit vectors and perpendicular to each other.
if(m_f_pitch_angle > 0.4f)
m_f_pitch_angle = 0.4f;
else if(m_f_pitch_angle < -0.4f)
m_f_pitch_angle = -0.4f;
if(m_f_yaw_angle > TwoPi) m_f_yaw_angle = -TwoPi;
else if(m_f_yaw_angle < -TwoPi) m_f_yaw_angle += TwoPi;
m_fv3_right = fvec3(1.0f,0.0f,0.0f);
m_fv3_up = fvec3(0.0f, 1.0f, 0.0f);
m_fv3_direction = fvec3(0.0f,0.0f, 1.0f);
Now we need to generate a matrix that defines the orientation of or camera which going to be relative to the object we going to attach the camera.
//rotate arbitrarily about the right base vector(x axis)
mat = mat.RotateArbitrary(m_f_pitch_angle, m_fv3_right);
m_fv3_up = m_fv3_up * mat;
m_fv3_direction = m_fv3_direction * mat;
//rotate arbitrarily up base vector(y axis)
mat = mat.RotateArbitrary(m_f_yaw_angle, m_fv3_up);
m_fv3_right = mat * m_fv3_right;
m_fv3_direction = mat * m_fv3_direction;
//now we can create our base vectors for camera rotation matrix.
cameraRotationMatrix.x.x = m_fv3_right.x;
cameraRotationMatrix.x.y = m_fv3_right.y;
cameraRotationMatrix.x.z = m_fv3_right.z;
cameraRotationMatrix.y.x = m_fv3_up.x;
cameraRotationMatrix.y.y = m_fv3_up.y;
cameraRotationMatrix.y.z = m_fv3_up.z;
cameraRotationMatrix.z.x = m_fv3_direction.x;
cameraRotationMatrix.z.y = m_fv3_direction.y;
cameraRotationMatrix.z.z = m_fv3_direction.z;
We are not over with the orientation of the camera yet. so far we calculated camera orientation based on what the pitch and yaw angles were. these values can be passed as part of user interaction when the player wants to move the camera. We need to now consider the displacement (or distance if you prefer) from the player. Also we want to create a rotation matrix that rotates the camera at the back of the object we want to attach to. so lets sort these out too.
//create local translation matrix that translates the camera at the given displacement from attached model.
//Note that displacement is just there to tell us the location of the camera relative to the attached model.
mat4 locTranslation = mat4::Translate(m_fv3_displacement);
//create local rotation matrix that rotates the camera at the back of the player.
mat4 localRot = localRot.yRotate(180);
Ok. So far so good. The next thing we want to do is to make this camera relative to the object we are attaching it to. We want this camera to be in an object space of an object. so this means that we need to transform our camera into the object space of the object we want to attach it to. The transformation matrix we need for this to happen is that the inverse Model matrix of the object. Thus we need to figure out this transformation matrix.
//we need to transform our view matrix into the object space of the model. To do this we need to get its inverse matrix
invModelrotation.x.x = m_modelRotation->x.x;
invModelrotation.x.y = m_modelRotation->x.y;
invModelrotation.x.z = m_modelRotation->x.z;
invModelrotation.y.x = m_modelRotation->y.x;
invModelrotation.y.y = m_modelRotation->y.y;
invModelrotation.y.z = m_modelRotation->y.z;
invModelrotation.z.x = m_modelRotation->z.x;
invModelrotation.z.y = m_modelRotation->z.y;
invModelrotation.z.z = m_modelRotation->z.z;
invModelrotation.Transpose();//inverse the rotation it should work since this is a matrix with orthogonal bases.
//inverse the translation
invModelTranslation.w.x = -modelPosition->x;
invModelTranslation.w.y = -modelPosition->y;
invModelTranslation.w.z = -modelPosition->z;
and finally we need to calculate the view matrix of the camera. this is the matrix we will be using it to multiply model matrices of every objects on the scene.
//now we can build our view matrix
m_viewMatrix = (invModelTranslation * invModelrotation) * (cameraRotationMatrix * (* localRot * locTranslation));
So reading from left to right. if we multiply a model matrix with this view matrix this is what will happen.
first, it will translate the object space of the model the camera attached.
then it will rotate to the attached model's orientation.
(invModelTranslation * invModelrotation).
next the model matrix will be transformed in to the camera space that is relative to the object space we attached the camera at.
PS: Im sure there is much efficient ways to this. However, for demonstration purpose I ignored many of them so that its easier to understand.
Edited by Nusakan, 28 February 2013 - 11:05 PM.