# OpenGL Camera and Viewing Transformations

## Recommended Posts

First of all, I am aware that there is no such entity called camera in OpenGL. The eye is located at the origin looking down the negative z-axis with the up vector being the positive y-axis (please correct me if I am wrong here or anywhere).

So here's the deal, I am trying to make a class that will handle the view of the scene using keyboard input. Just like in an aircraft, there is thrust which causes the camera to move forward(if we are sitting in a cockpit). Let us say, I am using W and S keys to control the thrust of this hypothetical camera and A and D keys to control my heading(the direction at which I am looking) and Up and Down keys to control the Pitch and Left and Right to control Roll.

If you have ever played Descent, there are actually ten ways to control the ship. Thrust forward, backward.. slide left, right, slide up and down, Pitch up and down, and roll left and right.

This is what I am trying to do here. By using basic opengl transformation functions I want to build my own camera class that I can control the view the scene very easily and how I want to view it.

Here is the code so far that I was able to do:
[CODE]
#ifndef CAMERA_H_INCLUDED
#define CAMERA_H_INCLUDED
#include <SDL.h>
#include <GLEW\glew.h>
#include <math.h>
#include "Misc.h"
const GLfloat INCREMENT_PITCH = 0.1f;
const GLfloat INCREMENT_ROLL = 0.1f;
const GLfloat INCREMENT_FORWARD = 1.0f;
// From Right to Left, in Bits, the keys are
// right left down up d a s w
class EyeCam
{
private:
GLfloat Angle_Pitch;
GLfloat Angle_Roll;
Uint8 KeyState;
public:
GLfloat eyeX, eyeY, eyeZ;
EyeCam(GLfloat eyeX, GLfloat eyeY, GLfloat eyeZ, GLfloat aimX, GLfloat aimY, GLfloat aimZ);
void onEvent(SDL_Event* event);
void update();
};
#endif // CAMERA_H_INCLUDED
[/CODE]

[CODE]

#include "EyeCam.h"
#include <iostream>
EyeCam::EyeCam(GLfloat eyeX, GLfloat eyeY, GLfloat eyeZ, GLfloat aimX, GLfloat aimY, GLfloat aimZ)
{
this->eyeX = eyeX;
this->eyeY = eyeY;
this->eyeZ = eyeZ;
Angle_Heading = Misc::toDegrees(atan2f((eyeX - aimX), (eyeZ - aimZ)));
GLfloat base = sqrt(pow(eyeZ-aimZ, 2) + pow(eyeX - aimX, 2));
Angle_Pitch = Misc::toDegrees(atan2f( -(eyeY - aimY), base));
//Angle_Roll = 0.0;
KeyState = 0x00;
}

void EyeCam::onEvent(SDL_Event* event)
{
if (event->type == SDL_KEYDOWN)
{
if (event->key.keysym.sym == SDLK_RETURN)
{
system("cls");
std::cout << "X: " << eyeX << '\n';
std::cout << "Y: " << eyeY << '\n';
std::cout << "Z: " << eyeZ << "\n\n";
std::cout << "Pitch: " << Angle_Pitch << '\n';
//std::cout << "Roll: " << Angle_Roll << '\n';
}
if (event->key.keysym.sym == SDLK_w)
KeyState = KeyState | 0x01;
if (event->key.keysym.sym == SDLK_s)
KeyState = KeyState | 0x02;
if (event->key.keysym.sym == SDLK_a)
KeyState = KeyState | 0x04;
if (event->key.keysym.sym == SDLK_d)
KeyState = KeyState | 0x08;
if (event->key.keysym.sym == SDLK_UP)
KeyState = KeyState | 0x10;
if (event->key.keysym.sym == SDLK_DOWN)
KeyState = KeyState | 0x20;
if (event->key.keysym.sym == SDLK_LEFT)
KeyState = KeyState | 0x40;
if (event->key.keysym.sym == SDLK_RIGHT)
KeyState = KeyState | 0x80;
}
else if (event->type == SDL_KEYUP)
{
if (event->key.keysym.sym == SDLK_w)
KeyState = KeyState & 0xFE;
if (event->key.keysym.sym == SDLK_s)
KeyState = KeyState & 0xFD;
if (event->key.keysym.sym == SDLK_a)
KeyState = KeyState & 0xFB;
if (event->key.keysym.sym == SDLK_d)
KeyState = KeyState & 0xF7;
if (event->key.keysym.sym == SDLK_UP)
KeyState = KeyState & 0xEF;
if (event->key.keysym.sym == SDLK_DOWN)
KeyState = KeyState & 0xDF;
if (event->key.keysym.sym == SDLK_LEFT)
KeyState = KeyState & 0xBF;
if (event->key.keysym.sym == SDLK_RIGHT)
KeyState = KeyState & 0x7F;
}
}
void EyeCam::update()
{
if (KeyState & 0x01) // w
{
eyeY = eyeY + INCREMENT_FORWARD * sin(Misc::toRadians(Angle_Pitch));
}
if (KeyState & 0x02) // s
{
eyeY = eyeY - INCREMENT_FORWARD * sin(Misc::toRadians(Angle_Pitch));
}
if (KeyState & 0x04) // a
{
}
if (KeyState & 0x08) // d
{

}
if (KeyState & 0x10) // Up
{
Angle_Pitch = Angle_Pitch + INCREMENT_PITCH;
}
if (KeyState & 0x20)
{
Angle_Pitch = Angle_Pitch - INCREMENT_PITCH;
}
if (KeyState & 0x40) // Left
{
Angle_Roll = Angle_Roll + INCREMENT_ROLL;
}
if (KeyState & 0x80) // Right
{
Angle_Roll = Angle_Roll - INCREMENT_ROLL;
}
glRotatef(-Angle_Pitch, 1.0, 0, 0);
glTranslatef(-eyeX, -eyeY, -eyeZ);
glRotatef(-Angle_Roll, 0.0, 0.0, 1.0);
}

[/CODE]

The constructor of the class takes the same arguments as gluLookAt() function, except for the Up vector (I've not implemented it yet in my class). The position of the eye in x, y, z coordinates and a point of aim in x, y, z coordinates.

In the constructor, I am calculating two angles, the Heading and the Pitch by using spherical trigonometry and then using these angles to rotate the view accordingly. Please note that, I am not using gluLookAt() function at all in my class.

Then, by using keyboard commands, I am just making changes to the angles to adjust my aim and calculating eye coordinates to adjust my position.

Now, the problem I am having here is that, If I am originally looking down the -ve z- axis and my up is +ve y axis. then I know to adjust my heading I have to Rotate the scene around the y - axis, if I want to adjust pitch I have to Rotate around the x - axis and if I want to adjust roll I have to Rotate around the z-axis. Hence, I am using equation of circle with heading angle to calculate my eyeX and eyeZ and pitch angle to calculate y - axis. But unfortunately, this will only work till I stay in the XZ plane. Any change in the plane, I will not get the desired results.

For example, at the start if I make a 90 degree Roll such that my up now is -ve x-axis, then if I want to Pitch up relative to the camera, I have rotate around the y-axis. Originally I was rotating about the x-axis when roll was 0. Similarly, If I am looking down the -ve z-axis and then roll will perfectly fine because I am rotating around the +ve z axis, however, If I move the camera to aim at the +ve x-axis and then roll it will not work, in that case I have to rotate on the -ve x-axis to make the roll work. I was able to solve this problem by changing the glRotate function for Roll

from:
glRotatef(Angle_Roll, 0, 0, 1);
to:

but what if I am looking down the -ve or +ve y-axis, this again will not work as desired and also I try to change the heading then it will automatically affect the roll transformation.

I hope that I am not confusing you guys too much with the my description, please correct me if I am wrong in my assumptions.

So, the bottom line question is, what I need to do make this camera work in every orientation so that it pitchs, rolls, and turns correctly relative to its orientation.

I am not very skilled with mathematics, but I have studied trigonometry and equations of curves and shapes. Please let me know If I need to cover more mathematics in order to implement what I want.

Any input will be appreciated, Thanks.

##### Share on other sites
If you are up for it, I recommend storing and manipulating your camera's orientation as a [url="http://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation"]quaternion[/url]. Especially if you are also implementing a roll for rotation, this will save you quite a bit of trouble. There are a few benefits of using quaternions:
- You gain the ability to Interpolate between two orientations, which is very valuable if you want to move your camera along a spline for instance.
- By not having separate angles, there are no "duplicate" poses (look up, and either rotate by yaw or roll).
- Concatenating rotations (say you want a camera fixed to an object, and you rotate the object) becomes a lot easier.

Regardless of what method of orientation you use, camera's will have some position and orientation in space. Combining them will give you the camera's transformation. To apply this to the scene, you will need to use the inverse of this transformation on the whole scene.
First imagine doing several rotations and translations on your camera. Then, as you "undo" each of those in the inverse order, but also apply them to your scene, you will end up with your camera back where it was, but your scene has moved back along with it. This is exactly what you want to achieve.

Let's take Euler angles as an example. In matrix form, you can view it like this:
Camera Transform = Txyz * Ry * Rp * Rr
where Txyz is the translation, and R denotes Rotation, yaw, pitch and roll. The inverse of this, is the following:
Inverse Transform = Rr[sup]-1[/sup] * Rp[sup]-1[/sup] * Ry[sup]-1[/sup] * Txyz[sup]-1[/sup].
Apply that to your scene, and you are done.

##### Share on other sites
IMHO one has to understand the concept of spaces. Statements like "looking along the negative z-axis", " rotating around the y axis", and "you'll end up with your camera back where it was" are somewhat meaningless without knowing the reference in use. Sometimes it becomes clear from the context, but often enough it doesn't do so.

E.g. when one applies the said inverse camera transformation (a.k.a. view transformation) to the entirety of objects including your camera, the scene doesn't really change. Instead, one is switching over to another reference system (usually called the view space) in which the camera is located at the origin and orientated normally. (Whether looking is done along the negative z-axis in view space or something else belongs to the projection matrix and clipping.)

When considering the concept of space, the questions of when to apply which transformation at what position in the pipeline becomes clear.

[quote name='Ignifex' timestamp='1345366628' post='4971044']
If you are up for it, I recommend storing and manipulating your camera's orientation as a [url="http://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation"]quaternion[/url]. ...
[/quote]
Only unit quaternions represent ure rotations, to be precise. This is important insofar that one need to normalize quaternions from time to time (not so often as rotation matrices need to be normalized, I agree).

[quote name='Ignifex' timestamp='1345366628' post='4971044']
- By not having separate angles, there are no "duplicate" poses (look up, and either rotate by yaw or roll).
[/quote]
A unit quaternion has 2 representations for each orientation, namely q and -q, so it isn't unique as well. Especially when doing interpolation one has the choice of following the short or the long arc (ignoring pathological cases here).

[quote name='Ignifex' timestamp='1345366628' post='4971044']
- Concatenating rotations (say you want a camera fixed to an object, and you rotate the object) becomes a lot easier.
[/quote]
Why this? Concatenation means to multiply quaternions on the one or matrices on the other hand. Spaces and order play their role regardless whether one uses quaternions or matrices.

IMHO especially the example "you want a camera fixed to an object" is less intuitive when using quaternions, because "fixed to an object" implies a fixed position, too. This can be expressed as a single homogenous matrix and a simple matrix product. Edited by haegarr

##### Share on other sites
I agree the concept of spaces is essential for a good understanding of how transformations work, especially in the case of view transformations where there are quite a few distinct spaces you have to understand (object space, world space, view/eye space, clip space).
IMO, there are many ways in which to view transformations. As you progress in your understanding of how these work, your view will likely change.
You might well be right that one should start by understanding spaces. How about reading something like [url="http://http.developer.nvidia.com/CgTutorial/cg_tutorial_chapter04.html"]this[/url].

My earlier post was mainly an attempt to intuitively explain some of the core mechanics of transformations, without diving too deep into the maths behind it.
[quote name='haegarr' timestamp='1345371483' post='4971060']
Why this? Concatenation means to multiply quaternions on the one or matrices on the other hand. Spaces and order play their role regardless whether one uses quaternions or matrices.
[/quote]
This is mainly a comparison between quaternions and euler angles. Finding the euler angles of a concatination from two rotations is not very optimal. I couldn't think of a proper use case where concatenating euler angles makes sense though, instead of the matrices derived from them.

Euler angles are good for allowing a user to specify an angle, but are far less useful when applying these angles as transformations.
Matrices are the default approach for applying transformations.
Quaternions are useful for manipulating rotation, such as interpolation.

##### Share on other sites
@Ignifex

As you suggested, I will take a look at quaternions, I never heard this term before.

As far as camera and inverse transformations are concerned, this is what I am trying to do here already, the knowledge that I have with Opengl and viewing transformations come from the red book.

From what I understand, there is no such thing called a 'Camera' in Opengl library. If you want view an object that is drawn and centered at the origin, then you can either use the gluLookAt(0, 0, +z, 0, 0, 0, 0, 1, 0) or insted you can use glTranslatef(0, 0, -z), both of these will have the same effect (for this case at least).

From what I have studied and understand, the function name " LookAt " is like a false advertisement. You are not actually looking somewhere in the scene, it just applies inverse transformations to the whole scene and you think that you are moving a camera in the scene when you are acutally just transforming the scene. But then again, I might be incorrect here and its a completely different debate.

In my class, I am doing a similar thing, taking vector input in euclidean space and then finding the appropriate angle of rotations of heading and pitch, and then apply the inverse of this when I am rotating about an axis and it works fine as I please.

The problem I am having, is that if you take a look at my code, as long you give the initial arguments such that your hypothetical camera is located at the XZ plane, the heading will work fine, however, If I pitch or Roll to 180 degrees, and then I apply my heading calculation, then they will seem to work in reverse order. If I give the input to turn left, it will turn right and vice versa, because I am still rotating around this +ve y-axis.

Please note that, when I was trying to implement this class, I was not thinking in terms of matrices, I was thinking in terms of these transformation commands (glRotatef and glTranslatef). Edited by uzipaz

## Create an account

Register a new account

• ## Partner Spotlight

• ### Forum Statistics

• Total Topics
627677
• Total Posts
2978591
• ### Similar Content

• Both functions are available since 3.0, and I'm currently using glMapBuffer(), which works fine.
But, I was wondering if anyone has experienced advantage in using glMapBufferRange(), which allows to specify the range of the mapped buffer. Could this be only a safety measure or does it improve performance?
Note: I'm not asking about glBufferSubData()/glBufferData. Those two are irrelevant in this case.
• By xhcao
Before using void glBindImageTexture(    GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format), does need to make sure that texture is completeness.
• By cebugdev
hi guys,
are there any books, link online or any other resources that discusses on how to build special effects such as magic, lightning, etc. in OpenGL? i mean, yeah most of them are using particles but im looking for resources specifically on how to manipulate the particles to look like an effect that can be use for games,. i did fire particle before, and I want to learn how to do the other 'magic' as well.
Like are there one book or link(cant find in google) that atleast featured how to make different particle effects in OpenGL (or DirectX)? If there is no one stop shop for it, maybe ill just look for some tips on how to make a particle engine that is flexible enough to enable me to design different effects/magic
let me know if you guys have recommendations.
• By dud3
How do we rotate the camera around x axis 360 degrees, without having the strange effect as in my video below?
Mine behaves exactly the same way spherical coordinates would, I'm using euler angles.
Tried googling, but couldn't find a proper answer, guessing I don't know what exactly to google for, googled 'rotate 360 around x axis', got no proper answers.

References:
Code: https://pastebin.com/Hcshj3FQ
The video shows the difference between blender and my rotation:

• By Defend
I've had a Google around for this but haven't yet found some solid advice. There is a lot of "it depends", but I'm not sure on what.
My question is what's a good rule of thumb to follow when it comes to creating/using VBOs & VAOs? As in, when should I use multiple or when should I not? My understanding so far is that if I need a new VBO, then I need a new VAO. So when it comes to rendering multiple objects I can either:
* make lots of VAO/VBO pairs and flip through them to render different objects, or
* make one big VBO and jump around its memory to render different objects.
I also understand that if I need to render objects with different vertex attributes, then a new VAO is necessary in this case.
If that "it depends" really is quite variable, what's best for a beginner with OpenGL, assuming that better approaches can be learnt later with better understanding?

• 11
• 12
• 10
• 12
• 22