Thread: Camera problem: rolling

  1. #1
    *******argv[] - hu? darksaidin's Avatar
    Join Date
    Jul 2003
    Posts
    314

    Camera problem: rolling

    I want to implement a camera that allows full 360 degree turns for yaw, pitch and roll. I'm finished with yaw and pitch, but roll seems to be a bit tricky =)

    The following code is supposed to produce the correct view- and up-vectors for the gluLookAt-function (and does).

    Code:
    vView.x= sin(fU) *cos(fV);
    vView.y= sin(fV);
    vView.z= cos(fV) *cos(fU);
    
    vUpVector.x= sin(fU) *cos(fV +PI/2);
    vUpVector.y= sin(fV +PI/2);
    vUpVector.z= cos(fV +PI/2) *cos(fU);
    
    gluLookAt(vPosition.x, vPosition.y, vPosition.z,
    	vView.x +vPosition.x, vView.y+vPosition.y, vView.z+vPosition.z,
    	vUpVector.x, vUpVector.y, vUpVector.z);
    x is right, y up and z points into the screen. fU and fV are angles that describe where the camera is looking at or if you so want, yaw and pitch in a flight simulator.

    My problem now is that I don't know how to roll the camera. I know I would have to rotate the up-vector around the viewvector and possibly also modify the viewvector because if the upvector has been rolled to point left (i.e.) then a pitch would result in a yaw, wouldn't it ?...



    I guess what I'm asking for is, if somebody can tell me how to rotate a vector around it's normal (that's what I need to do, right?). This and any other comments would be very appreciated.


    btw, I know the code is not at all optimized. I will store sine and cosine in a variable and use that instead of calling the functions again and again once rolling is implemented. I could also write -sin(fV) instead of cos(fV +PI/2), but my primary aim is working code and thats not yet accomplished :/

  2. #2
    Pursuing knowledge confuted's Avatar
    Join Date
    Jun 2002
    Posts
    1,916
    The way I did camera roll was like this:

    Keep a variable representing camera roll in radians (or degrees, I suppose, but you'd have to convert)

    Generate the matrix for the camera (your view matrix now) as you already are. Then, generate a matrix for rotation on the Z axis, using the camera roll variable. Multiply the matrix you have in your current code by this rotation matrix, and use that as your view matrix.

    As far as I can tell, that works fine and I don't think I have any problems with gimbal lock on the camera (I haven't thoroughly tested for gimbal lock with the camera though - if it's there, I'll discover it later) I'm also using DirectX to generate the "camera matrix," though. The function takes location, look at, and up vectors, and I've coded in a way to convert an orientation vector into those other three, allowing orientation to be controlled by the mouse. I'd be glad to let you have a look at my camera class if you'd like.
    Away.

  3. #3
    *******argv[] - hu? darksaidin's Avatar
    Join Date
    Jul 2003
    Posts
    314
    Originally posted by blackrat364
    Keep a variable representing camera roll in radians (or degrees, I suppose, but you'd have to convert)

    Generate the matrix for the camera (your view matrix now) as you already are. Then, generate a matrix for rotation on the Z axis, using the camera roll variable. Multiply the matrix you have in your current code by this rotation matrix, and use that as your view matrix.
    Thanks for the reply!
    Do you mean you calculate yaw and pitch by hand for DX's lookAt function, let it calculate the matrix and then generate another matrix only for roll? I mean ok, it's just once per frame, but... mhm...


    Originally posted by blackrat364
    As far as I can tell, that works fine and I don't think I have any problems with gimbal lock on the camera (I haven't thoroughly tested for gimbal lock with the camera though - if it's there, I'll discover it later) I'm also using DirectX to generate the "camera matrix," though. The function takes location, look at, and up vectors, and I've coded in a way to convert an orientation vector into those other three, allowing orientation to be controlled by the mouse. I'd be glad to let you have a look at my camera class if you'd like.
    Of couse I'd be glad if I could have a look at the class!
    However, I think I'd like to perform all the camera rotations by normal angular functions, unless that's really as complicated as I am beginning to think it is...

    edit: btw, what does gimpal lock mean ? Google came up with a very strange translation... to say the least.
    edit2: oops, just read your thread and somebody explained it....
    Last edited by darksaidin; 08-10-2003 at 05:14 PM.

  4. #4
    Pursuing knowledge confuted's Avatar
    Join Date
    Jun 2002
    Posts
    1,916
    Well, DX has the following function, which will generate what would be the view matrix, if you didn't want to incorporate camera roll.
    D3DXMatrixLookAtLH(&matCamera,&eye,&look_at,&up)
    matCamera is a matrix, and eye, look_at, and up are all vectors. The function returns the "camera matrix" in the matCamera matrix.

    After I have that matrix, I use
    D3DXMatrixRotationZ(&matRoll,fRoll);
    which returns a matrix representing the roll of the camera on the Z axis. Then I use matCamera*matRoll for the view matrix. This works, because all you really want the camera roll to do is turn the camera (which is always at (0,0,0) in camera space) on the Z axis - like in the blair witch project, lol) A matrix is generate which completely ignores the roll, which is what you have now, and my matCamera matrix. All you have to do is multiply that by a matrix which rolls it. I don't think it's very costly in computation, and it was definitely the easiest way to do it. It might seem like it's more than what's needed (not sure if it even is), but it's two matrices and in the course of rendering one frame in a scene, you'll be setting many, many matrices, for each and every single object in the world. So it shouldn't be a big performance problem for the camera.

    I've attached my camera class, hopefully you'll be able to understand it. What I was calling matCamera above is called matView in the code - I implemented that part before I thought of doing the roll. There might be a few confusing parts, such as the process() function, which uses the orientation to generate a new look_at vector. Also, the function getviewtransform() might confuse you - it's just returning matView*matRoll.

    gimbal (note: that's a b, not a p) lock is caused by using three matrices to represent the rotation on each axis. They're multiplied together in a certain order, for example:
    matRotationX*matRotationY*matRotationZ
    The X axis isn't transformed by rotation on Y or Z, and the Y axis isn't transformed by rotation on Z. It makes it possible to rotate one axis onto another axis (making X and Z the same axis), causing you to loose a degree of freedom. Quaternions are needed to avoid it... I'm still working on getting those implemented entirely correctly in my transformation class.
    Away.

  5. #5
    Banned
    Join Date
    Jan 2003
    Posts
    1,708
    the code I used to rotate a vector about an arbitrary axis...everything is already normalized, works even if they aren't orthogonal
    EDIT:
    and I don't think this can cause gimbal lock
    Code:
    void	RotateVector(float	Angle, Vector3	*VecToRotAbout, Vector3	*VecToRotate)
    {
    	float cosTheta = (float)cos(Angle);
    	float sinTheta = (float)sin(Angle);
    *VecToRotate=
    Perp(VecToRotate,VecToRotAbout)*cosTheta+
    (CrossProduct(*VecToRotAbout, *VecToRotate)*sinTheta) 
    +Parallel(VecToRotate, VecToRotAbout); 
    }
    and the individual functions that make it happen:

    Code:
    Vector3	Perp(Vector3	*a, Vector3	*axis)
    {
    	Vector3	temp = *a - Parallel(a, axis);
    	return	temp;
    }
    
    Vector3	Parallel(Vector3	*a, Vector3	*axis)
    {
    	Vector3	temp = *axis * DotProduct(a, axis);
    	return	temp;
    }
    Last edited by Silvercord; 08-10-2003 at 06:00 PM.

  6. #6
    *******argv[] - hu? darksaidin's Avatar
    Join Date
    Jul 2003
    Posts
    314
    Blackrat: You are right, the way you did it is probably the easiest sollution to the problem since opengl has very similar commands (gluLookAt etc.). It's maybe even the fastest way but for now I'll try to do it without a matrix. Event if it's only to realize that a matrix operation would have been 5000x times faster


    Silvercord: Yes, that actually works quite good. Thanks! I used it to rotate the upvector around the viewvector (the rolling).

    One problem remains though: the camera rotation is still independent of this vector because I need to calculate it first.
    Lets say I roll the camera by 45 degress to the left and then rotate around the z axis. This should should resolve to a rotation around both, z and x.

    mhm, it's 4 o'clock in the morning here. I better get some sleep now.

  7. #7
    Pursuing knowledge confuted's Avatar
    Join Date
    Jun 2002
    Posts
    1,916
    With a standard coordinate system, x is left/right, y is up/down and z is into the screen. So... roll should be rotating on Z, not on X - unless you have X going into the screen.

    Anyway, I just wanted to give you a warning about doing the roll silvercord's way (not saying it's bad). Make sure that your strafing vectors are perpendicular to the way you're looking and such. Changing the X values isn't going to cut it. You'll want to do this:
    Code:
    strafe_direction = (look_at - eye) X up; //X means cross product
    You may or may not already have that. Also, when "jumping" or moving the camera vertically, make sure you're adding/subtacting the up vector from the camera position, not just changing the y value.
    Away.

  8. #8
    Banned
    Join Date
    Jan 2003
    Posts
    1,708
    yeah, I probably should've mentioned that, thanks black.

  9. #9
    *******argv[] - hu? darksaidin's Avatar
    Join Date
    Jul 2003
    Posts
    314
    Originally posted by blackrat364
    With a standard coordinate system, x is left/right, y is up/down and z is into the screen. So... roll should be rotating on Z, not on X - unless you have X going into the screen.
    Maybe I didn't express it very well. Rolling happens around the cameras z axis. That is true. But after you rolled (let's say 45 degrees to the left), all subsequent rotations (not rolls, rotations by U and V) need to be modified by this value. I.e. a rotation around the y-Axis would resolve to a rotation around both, y and x.
    It's really hard to explain what I mean. Did you ever play Descent? Check it out: rotate the vehicle around y. Then roll it around z. The next rotation will then rotate you around x and y.


    Originally posted by blackrat364
    Anyway, I just wanted to give you a warning about doing the roll silvercord's way (not saying it's bad). Make sure that your strafing vectors are perpendicular to the way you're looking and such. Changing the X values isn't going to cut it. You'll want to do this:
    Code:
    strafe_direction = (look_at - eye) X up; //X means cross product
    Yes, I already strafe along the crossproduct of upvector and view vector. My view vector is (look_at - position).

    Originally posted by blackrat364
    You may or may not already have that. Also, when "jumping" or moving the camera vertically, make sure you're adding/subtacting the up vector from the camera position, not just changing the y value.
    Not sure about this. I haven't any jumping code in yet, just because there is no collision detection as of now, but once I have that ready I wanted to jump along the gravity vector, not the upvector.

  10. #10
    Banned
    Join Date
    Jan 2003
    Posts
    1,708
    Just a suggestion, I keep all of my vectors normalized and at the origin, i.e my view vector is at the origin, as is my strafe and up vectors. That way you don't have to do view-position every time you want to rotate or extract the viewing direction, and all you have to do is calculate position+view before passing to glulookat

  11. #11
    *******argv[] - hu? darksaidin's Avatar
    Join Date
    Jul 2003
    Posts
    314
    mhm, I think I'll give it up. It just doesn't work. Without rolling it's no problem at all. I can rotate the camera full 360 degrees around x and y. I can fly a looping. yeah =)

    Thanks to your rotateVector() I can also rotate the upvector around the view vector which looks like rolling but doesn't behave like it. I guess that has to do it.

  12. #12
    Pursuing knowledge confuted's Avatar
    Join Date
    Jun 2002
    Posts
    1,916
    Hmmm... for the problem you mentioned earlier about rotating the axes w/ camera roll, my method will still work perfectly. It might just be the easiest way
    Away.

  13. #13
    Pursuing knowledge confuted's Avatar
    Join Date
    Jun 2002
    Posts
    1,916
    Darksaidin - you can ignore what I'm about to say. It's for Silvercord.

    Code:
    //your arbitrary axis rotation code
    void	RotateVector(float	Angle, Vector3	*VecToRotAbout, Vector3	*VecToRotate)
    {
    	float cosTheta = (float)cos(Angle);
    	float sinTheta = (float)sin(Angle);
    *VecToRotate=
    Perp(VecToRotate,VecToRotAbout)*cosTheta+
    (CrossProduct(*VecToRotAbout, *VecToRotate)*sinTheta) 
    +Parallel(VecToRotate, VecToRotAbout); 
    }
    How many times do you call this? Once, or once for each axis (x,y,z)? If you call it once, you more or less already are using quaternions, but I don't think you would be able to pull it off without making the total conversion over to quaternions. So, I'm thinking that you call it three times, with the second getting the modified vector from the first call, and the third call getting the modified vector from the second. If you're indeed calling it three times, it can indeed cause gimbal lock. Assuming you are doing it X,Y, then Z, rotate 90 degrees (.5pi radians) on Y, then try rotation on X and Z. It might gimbal lock. Test it out.
    Away.

  14. #14
    Banned
    Join Date
    Jan 2003
    Posts
    1,708
    only twice

    and never about the x or z

    i.e I figure out how many radians to rotate about each axis, and then i rotate the view vector about the strafe vector, then about the y axis

    Code:
    	RotateVector(XDegRad, &mStrafe, &mView);
    	RotateVector(YDegRad, &mYAxis, &mView);
    
    	mUp = CrossProduct(mStrafe, mView);
    so if i just combine those two into a single rotation it's basically a quaternion?
    Last edited by Silvercord; 08-11-2003 at 03:24 PM.

  15. #15
    *******argv[] - hu? darksaidin's Avatar
    Join Date
    Jul 2003
    Posts
    314
    Originally posted by blackrat364
    Hmmm... for the problem you mentioned earlier about rotating the axes w/ camera roll, my method will still work perfectly. It might just be the easiest way
    I use a clean matrix now and load it into the modelview every frame. I also put all that stuff that gluLookAt does into it, so I don't need to modify the matrix twice. Not for performance reasons, but just because I didn't like such a "mixed code" =) That was the main reason why I didn't want to use matrices in the first place. It also has the advantage that I don't need the glu library anymore =)

    The camera itself is a bit hard to control now. I think games like Descent do something to the rotation to stabilize rolling. I had a look at it (didn't remember the graphic was *that* awful) and it seems like every movement or rotation also reduces the "rollangle" a little bit. But thats too much for me to do now. The camera works fine. Only thing is that it sucks for FPS games.
    To allow the game-engine to support both cameras, I created an abstract baseclass and allow the user to register a specific camera at the engine.

    Thanks for all your help, Black and Silver!

    edit:

    Originally posted by blackrat364
    How many times do you call this? Once, or once for each axis (x,y,z)? If you call it once, you more or less already are using quaternions, but I don't think you would be able to pull it off without making the total conversion over to quaternions. So, I'm thinking that you call it three times, with the second getting the modified vector from the first call, and the third call getting the modified vector from the second. If you're indeed calling it three times, it can indeed cause gimbal lock. Assuming you are doing it X,Y, then Z, rotate 90 degrees (.5pi radians) on Y, then try rotation on X and Z. It might gimbal lock. Test it out.
    I used Silvers rotatevector for my upvector. That worked well. But I also tested what you describe and yes, it does not work. After half a turn things get really really messy. I assume thats what you mean with gimpal lock (not sure though, I didn't yet have the chance to learn a bit more about that problem).
    Last edited by darksaidin; 08-11-2003 at 04:01 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. WS_POPUP, continuation of old problem
    By blurrymadness in forum Windows Programming
    Replies: 1
    Last Post: 04-20-2007, 06:54 PM
  2. Model orientations in 3D
    By VirtualAce in forum Game Programming
    Replies: 11
    Last Post: 11-14-2006, 12:21 AM
  3. directx problem with camera , nothing happens
    By Anddos in forum Game Programming
    Replies: 1
    Last Post: 04-10-2006, 03:14 AM
  4. Laptop Problem
    By Boomba in forum Tech Board
    Replies: 1
    Last Post: 03-07-2006, 06:24 PM
  5. Replies: 5
    Last Post: 11-07-2005, 11:34 PM