# Thread: Camera problem: rolling

1. ## 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. 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.

3. 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....

4. 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.

5. 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;
}```

6. 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. 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.

8. yeah, I probably should've mentioned that, thanks black.

9. 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. 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. 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. 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

13. 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.

14. 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?

15. 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).

Popular pages Recent additions