Skip to main content
GameDev.net gamedev.net
🔒 Locked

Changing camera pitch

Started by meo Dec 27, 2004 at 5:40 PM 10 replies 1.8k views
Original Post
meo
meo
I'm trying to make an fps engine and have a problem that's keeping me busy. My camera's rotations/translations are working well except for one thing changing the camera's pitch up/down. This works until a certain point: when i try to rotate(jaw) the camera after a pitch the camera seems to roll which is not my intention. For example: when i set the camera to look 40° up and then rotate(jaw) for 180° the camera looks 40° down! What am i doing wrong? This is my code, after the rotate function i extract my left,up,dir vector from matrix to build my viewing matrix.
 
void rotate(float angle,float x, float y, float z)
{
	CMatrix mx,my,mz;

	 mx = matrix.rotate(angle,x,0,0);
	 my = matrix.rotate(angle,0,1,0);
	 mz = matrix.rotate(angle,0,0,z);

CMatrix mt = mx*my*mz;

matrix = matrix*mt;
}

CMatrix rotate(double angle, int x, int y, int z)
{
   CMatrix m;
   float sine = (float)sin(angle);
   float cosine = (float)cos(angle);

   if(x)
      {
	      m.matrix[5] = cosine;
	      m.matrix[6] = sine;
	      m.matrix[9] = -sine;
	      m.matrix[10] = cosine;
      }

   if(y)
      {
	      m.matrix[0] = cosine;
	      m.matrix[2] = -sine;
	      m.matrix[8] = sine;
	      m.matrix[10] = cosine;
      }

   if(z)
      {
	      m.matrix[0] = cosine;
	      m.matrix[1] = sine;
	      m.matrix[4] = -sine;
	      m.matrix[5] = cosine;
      }

return m;
}


Zakwayda
Zakwayda
Before trying to figure out your problem, I have a couple of questions about your code. (It might be perfectly clear to others, but I seem to be a bit 'slow' today.)

Is the bottom 'rotate' function the one being called from the top function? I wouldn't think so, because the function called from the top one looks like a member function. Plus, the arguments are floats and the lower function takes ints.

If the lower function is not the one the top function is calling, we probably need to see the function CMatrix::rotate().

In the lower function, I take it you intend to send a 'true' value only for the axis you want to rotate around? You could send multiple true values, but it doesn't seem that the result would make much sense. If your intent is simply to specify the axis around which to rotate, a named constant would be better here.

I didn't check the matrices themselves (i.e. where you're putting the sins and cosins), so I can't tell you if they're right or not.

I assume the top function will make more sense once we know what CMatrix::rotate() looks like. Still, the function only takes a single angle argument. Is that supposed to be yaw, or pitch? It looks like you're constructing x, y and z matrices all with the same angle, which doesn't make immediate sense to me.

Anyway, that's probably enough questions. With some clarification, I'm sure we can solve your problem.
ajas95
ajas95
Quote:
CMatrix mt = mx*my*mz;


try:
CMatrix mt = my*mx*mz;


You want to pitch the camera around its local X-axis, not world X. Whereas, you want to yaw around world Y, not local Y.
meo
meo
Quote:
Original post by jyk
Before trying to figure out your problem, I have a couple of questions about your code. (It might be perfectly clear to others, but I seem to be a bit 'slow' today.)

Is the bottom 'rotate' function the one being called from the top function? I wouldn't think so, because the function called from the top one looks like a member function. Plus, the arguments are floats and the lower function takes ints.

If the lower function is not the one the top function is calling, we probably need to see the function CMatrix::rotate().

In the lower function, I take it you intend to send a 'true' value only for the axis you want to rotate around? You could send multiple true values, but it doesn't seem that the result would make much sense. If your intent is simply to specify the axis around which to rotate, a named constant would be better here.

I didn't check the matrices themselves (i.e. where you're putting the sins and cosins), so I can't tell you if they're right or not.

I assume the top function will make more sense once we know what CMatrix::rotate() looks like. Still, the function only takes a single angle argument. Is that supposed to be yaw, or pitch? It looks like you're constructing x, y and z matrices all with the same angle, which doesn't make immediate sense to me.

Anyway, that's probably enough questions. With some clarification, I'm sure we can solve your problem.


Well it is indeed possible that my code is't very clear. I just pasted the two main functions i use to make a rotation, these
funtions are not in the same source file.

The bottom 'rotate' is actually inlined and is indeed being called from the top function. I'm aware of the int/float inconsistencies, i will deal with this later when i get the camera working.

I intention is to build the rotation matrix for each axis(x,y,z) and then multiply them together 'CMatrix mt = mx*my*mz' the result gets multiplied with the camera 'matrix = matrix*mt;'

This DOES work except when i pitch the camera followed by a jaw, the camera seems to roll. I do this by calling 'rotate(0.01,1,0,0)' (pith around x axis) followed by 'rotate(0.01,0,1,0)' (jaw around y axis).

I tryed swithing the multiplication order of 'CMatrix mt = mx*my*mz' which did not work.

[Edited by - meo on December 28, 2004 6:39:26 AM]
ajas95
ajas95
Oh yeah, if you're concatenating your camera rotations you end up with
matrix3 = mx1 * my1 * mz1 * mx2 * my2 * mz2.

Really, you want:
my1 * my2 * my3 * mx1 * mx2 * mx3 * mz1 * mz2 * mz3.

One thing that will probably work, but might get a little weird if you start adding roll is:
CMatrix mt = mx*mz;matrix = my*matrix*mt;


if that doesn't work... then we'll have to look harder.
meo
meo
Quote:
Original post by ajas95
Oh yeah, if you're concatenating your camera rotations you end up with
matrix3 = mx1 * my1 * mz1 * mx2 * my2 * mz2.

Really, you want:
my1 * my2 * my3 * mx1 * mx2 * mx3 * mz1 * mz2 * mz3.

One thing that will probably work, but might get a little weird if you start adding roll is:
CMatrix mt = mx*mz;matrix = my*matrix*mt;


if that doesn't work... then we'll have to look harder.

I tried this too but doesn't work.
I'm wondering is this problem the famous gimbal lock ? If it is i
probably have to change to quaternions.

You can download my .exe from http://users.telenet.be/meo/pro1.rar to see what's going on. page up/down changes pitch, arrow keys to rotate/move cam.

It is coded in .net so you will need the net runtime files.
ajas95
ajas95
No, the problem is not gimbal lock. The problem is that you're yawing around the local y-axis. That's what happens when you do:

matrix = matrix * my;

instead, you want to yaw around the world y-axis which is:

matrix = my * matrix;

That's really all there is to it. When you try these things, you're at least getting different wrong results right? I mean, it's not like you're compiling one .exe and then running a stale one to test...
meo
meo
Quote:
Original post by ajas95
No, the problem is not gimbal lock. The problem is that you're yawing around the local y-axis. That's what happens when you do:

matrix = matrix * my;

instead, you want to yaw around the world y-axis which is:

matrix = my * matrix;

That's really all there is to it. When you try these things, you're at least getting different wrong results right? I mean, it's not like you're compiling one .exe and then running a stale one to test...


Well you are right, i tried this:

matrix = matrix*mx;
matrix = my*matrix;
matrix = matrix*mz;

and this fixes the pitching up and down but...now when i jaw, the world is jawed around world Y axis (like you said). This is not wat i want, the world has to be jawed around the camera's Y-axis(maintaining the pith).
thedustbustr
thedustbustr
This topic I started when I had a similar (the same?) problem will probably be helpful.

To quote Dmytry's extremely helpful response:
Quote:
In real world, get something in shape of cube, and paint x+ y+ z+ x- y- z- marks on it Ideally, place x,y,z sticks in it.

Then, rotate it around it's local x axis so it looks slightly up(pitch it). Then rotate it around local y axis(yaw). hat you see? It's now rotated like it have roll. It's not a math problem, it's math feature.It's how our world works.
If you want it to rotate w/o roll you have to rotate it around global y axis. But there's "problem with non-standart orientations", just because, that model is also wrong. In real world you have body and head. Head rotates in coordinate system of body, not in it's own coordinate system and not in global coordinate system.


If you are flying a space ship around like in Descent, you want to rotate around the camera/local y axis. If you want FPS Human style movement like noclip mode or observer mode in UT2004 or CS, you want to rotate around the global y axis.

As you seem to want to preserve the pitch, you probably want the Descent style camera. If this is the case, the induced roll is supposed to be there: Descent had a menu option to "autolevel" your spaceship so that it rolls back to a normal orientation consistent with the world (without roll). Alternately, you could bind two keys to roll left and roll right (or joystick twist) and manually do it.

To quote your original topic,
Quote:
Original post by meo
when i try to rotate(jaw) the camera after a pitch the camera seems to roll which is not my intention. For example: when i set the camera to look 40° up and then rotate(jaw) for 180° the camera looks 40° down!
What am i doing wrong?

You aren't doing anything at all wrong, if the only problem you have is undesired roll: it is normal. Try implementing auto-leveling (a hackish solution) if you want to get rid of it. Auto-leveling basically will take your rolled camera and over time rotate it to its normal roll (or zero roll) without any user input: it will look like it is magically rolling. Dmytry posted how to do this:
Quote:
If you don't mind camera rotating by itself without player pressing keys, you can do it so: compute global UP vector in camera's coordinates(thansform 0,1,0 by camera rotation matrix you use to transform from world coordinate system to camera coordinate system) and then find cross product of global UP and [0,1,0] , then roll your camera by z-component of that cross product * some_small_constant*dt (roll,that is, in local system).


Here is my own implementation of Dmytry's technique for autoleveling:
//auto-leveling    float dt=clock.dt();    static const k=1;    vector3 global_up=vector3(0,1,0)*WorldToLocal;    vector3 v=CrossProduct(vector3(0,1,0),global_up);    roll(v.z*k*dt);


HTH.
meo
meo
Thanks for the help but i don't want a camera like Descent, it has to be a
first person camera like in Quake.

I changed my matrix multiplication to my*matrix like ajas95 told me and when i pitch everyting looks fine. But when i jaw the world seems to rotate around a center world axis instead of rotating around the camera.
So now the Up axis is ok but it isn't placed on the position of the camera.
So how do i rotate around the up axis of the camera without losing my pitch (up or down)

I update my exe on http://users.telenet.be/meo/pro1.rar to make things more clear.
ajas95
ajas95
Your camera matrix must have a translation component...

So you need to subtract that out, then do the rotation multiply, then add the translation back in. That way your camera will rotate in place.
meo
meo
Solved! It works, thank you all for your help.

2 things to remember;

-rotate around global up vector
-extract position before rotation and plug it back in the matrix after rotation

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.