Modifying the view matrix

Started by
11 comments, last by Trienco 12 years, 6 months ago
I have a free camera class used for observing 3D models in a demo of mine. It contains the view and projection matrices I pass to my shaders for rendering. So far I've been storing position, pitch and yaw as member variables inside the camera class and then on update I've been doing something like this to create my view matrix for rendering:


mPitch += GetControllerPitch();
mYaw += GetControllerYaw();

Matrix rotation = Matrix::CreateXRotation(mPitch);
rotation = Matrix::CreateYRotation(mYaw) * rotation;

Vector& left = rotation.GetColumn(0);
Vector& up = rotation.GetColumn(1);
Vector& forward = rotation.GetColumn(2);

if (GetControllerUp())
mPosition += forward;
if (GetControllerDown())
mPosition -= forward;
if (GetControllerLeft())
mPosition -= left;
if (GetControllerRight())
mPosition += left;

mView = Matrix::CreateLookAt(mPosition, mPosition+forward, up);


I don't like this, though. I don't like storing the total position, pitch and yaw values in the class. I should be able to get all this information directly from the view matrix, shouldn't I? I'm effectively storing the information twice. I was wondering if I can do away with calling CreateLookAt() every frame and just multiply the view matrix by the relative changes each frame. Something like this:


float pitchChange = GetControllerPitch();
float yawChange = GetControllerYaw();

Matrix rotation = Matrix::CreateXRotation(pitchChange);
rotation = Matrix::CreateYRotation(yawChange) * rotation;

mView = rotation * mView;

Vector& left = mView .GetColumn(0);
Vector& up = mView .GetColumn(1);
Vector& forward = mView .GetColumn(2);
Vector& position = mView.GetColumn(3);

if (GetControllerUp())
mPosition += forward;
if (GetControllerDown())
mPosition -= forward;
if (GetControllerLeft())
mPosition -= left;
if (GetControllerRight())
mPosition += left;


Should this be possible or do I need to take other things into consideration?
Advertisement
Probably the part where the view matrix is the inverse of the cameras transformation, so if you want to use only the view matrix without inverting back and forth all the time, you will have to apply everything in reverse order and direction.
f@dzhttp://festini.device-zero.de
What speaks against storing these values directly in the class? Saving a few bytes of
space? Making your class feel more
'professional'? Maybe its just me, but it feels akward using the view matrix to store and access camera data.

What speaks against storing these values directly in the class? Saving a few bytes of
space? Making your class feel more
'professional'?


I think that's it, yeah. I want to do things the "proper" way, really. Plus, I think I'd like to get more of a feel for how transformations affect the view matrix. If I use CreateLookAt() every frame then all of that is abstracted from me.

I'm at work at the minute but I'll try applying the inverse of the rotation and translation changes when I get home and see if I have any luck. Presumably all I need to do is reverse the direction supplied by the controller? I.e., if they press left on the controller then translate by positive X rather than negative X?
You also need to reverse the order of transformations. You can't easily do that in your case, because you reduced the whole multiplication with a translation matrix to directly modifying the position.

Also, lookAt doesn't hide much. It builds a transformation matrix that is right/up/forward/position (just what you already have anyway) and then inverts it (usually in a simplified way, because you don't care about a generic matrix inversion for this.
f@dzhttp://festini.device-zero.de

You also need to reverse the order of transformations. You can't easily do that in your case, because you reduced the whole multiplication with a translation matrix to directly modifying the position.

Also, lookAt doesn't hide much. It builds a transformation matrix that is right/up/forward/position (just what you already have anyway) and then inverts it (usually in a simplified way, because you don't care about a generic matrix inversion for this.


Hmm. I think I've got it basically working but I'm getting a anomaly. After I've got the relative changes to pitch, yaw and translation I'm doing this to get the resulting view matrix:


Matrix translation = Matrix::CreateTranslation(xChange, yChange, zChange);
Matrix rotation = Matrix::CreateXRotation(pitchChange);
rotation = Matrix::CreateYRotation(yawChange) * rotation;

Matrix transform = rotation * translation;

mView = transform * mView;


Does this look ok? I'm not sure why I was setting the position explicitly before. Anyway, the anomaly I'm getting is that the translation seems to work fine but when I pitch up and attempt to rotate to yaw around the scene it looks like it's rolling too. I've recorded a small video so you can see what I mean as it's easier than trying to explain it:

[media]
[/media]

At first I'm just translating but then I pitch up you can see that when I'm rotating the yaw it appears to be rolling around to the side whereas what I'm wanting is it to rotate around the scene like a free look camera. I suspect it's not actually an anomaly but more than I'm missing something in my calculations.
There are still two things. a) you didn't invert your order of rotation and b) you are applying your new transformation after instead of before the old transformation. Matrix muliplication is NOT commutative and A*B is not the same as B*A, in fact, you can think of A*B applying A using local coordinates and B*A applying A in world coordinates (ie. one rotation uses the objects current local axes and the other uses the worlds x,y,z axes).

But your biggest problem is that before you were building you rotation from scratch every frame and always just summed up the angles. Now you apply the new rotation to the previous rotation, which results in a totally different order of rotations and obviously doesn't get you the result you want. To get the same effect, one of the rotations needs to use a global axis (typical first person cameras will always use the worlds (0,1,0) because it would be plain wrong to rotate around the objects actual "up").
f@dzhttp://festini.device-zero.de
There are still two things. a) you didn't invert your order of rotation and b) you are applying your new transformation after instead of before the old transformation. Matrix muliplication is NOT commutative and A*B is not the same as B*A, in fact, you can think of A*B applying A using local coordinates and B*A applying A in world coordinates (ie. one rotation uses the objects current local axes and the other uses the worlds x,y,z axes).

But your biggest problem is that before you were building you rotation from scratch every frame and always just summed up the angles. Now you apply the new rotation to the previous rotation, which results in a totally different order of rotations and obviously doesn't get you the result you want. To get the same effect, one of the rotations needs to use a global axis (typical first person cameras will always use the worlds (0,1,0) because it would be plain wrong to rotate around the objects actual "up").
f@dzhttp://festini.device-zero.de
Yeah, I know that matrix multiplication isn't commutative. I've inverted the order of the rotation like this...


Matrix rotation = Matrix::CreateYRotation(yawChange);
rotation = Matrix::CreateXRotation(pitchChange) * rotation

...but it made no difference to the actual rotation.

When you say I'm applying the new translation after the old one do you mean I should be doing this...


mView = mView * transform;

? Because when I do this I get really strange results. The translation looks ok but the rotation is way off what I expect.

I'm using column-major pre-multiplied matrices which means that the order of multiplication goes from right to left, rather than left to right. Case in point, in the video you can see that the triangle has been translated away from the origin but it's rotating about it local Y axis. The transform for this is transform = translation * rotation. This performs the rotate and then the translation. If I did transform = rotation * translation then the triangle would rotate around the origin at a distance specified by the translation.

How can I perform the rotation about the world's up? I'm thinking I need to calculate the rotation change in local space (like I already am) and then multiplying that by the inverse of the rotation that's applied to the view matrix, maybe? Though I'm not sure how I'd do that even if it is the right thing to do.
I think you're right about the rotation about the world. From what I can gather my rotations are working correctly. I just imagined what it would look like if I was a camera and I pitched up around an object and then rotated my head left and right to simulate the yaw and what I'm seeing looks about right. I think what I actually want is a first person camera that can fly around the scene rather than a free camera. Which I imagine is where your rotation about the world idea comes in?

This topic is closed to new replies.

Advertisement