360 likes | 936 Views
A Camera Class for OpenGL. John McGuiness October 2006. Necessity for a Camera Class. Existing available camera tool - gluLookAt() Basic utility which encapsulates a series of rotate and translate commands Allows viewing along an arbitrary line of sight with an “Up” vector defined
E N D
A Camera Class for OpenGL John McGuiness October 2006
Necessity for a Camera Class • Existing available camera tool - gluLookAt() • Basic utility which encapsulates a series of rotate and translate commands • Allows viewing along an arbitrary line of sight with an “Up” vector defined • Need extra transformations to provide greater flexibility • Need to modify “Forward” and “Along” vectors as well as “Up” to improve on gluLookAt() • Camera class may be built to encapsulate commands for greater ease of use
“Up”, “Forward” and “Along” • The three camera view vectors are defined as shown:
Proposed Camera Features • The camera class should: • Provide motion along the view vectors as well as arbitrary axes (in some cases) • Provide rotation about the view vectors as well as arbitrary axes (in some cases) • Maintain the camera’s own orientation by keeping the viewing vectors orthogonal to each other • Need to define motion for two possible types of camera: • Land camera – e.g. for road vehicles simulation • Air camera – e.g. for flight simulation
Camera Motion • Walking • This is motion along the Forward vector (or Z-axis):
Camera Motion • Strafing • This is side to side motion on the Along vector (or X-axis):
Camera Motion • Flying • This is vertical motion on the Up vector (or Y-axis):
Pitching This is rotation about the Along vector – looking up and down Camera Rotation
Yawing This is rotation about the Up vector – looking left and right Camera Rotation
Rolling This is rotation about the Forward vector – twisting left and right Camera Rotation
Camera Class Declaration • The camera class uses a type called Vector3D which provides storage and common operations (e.g. dot product, cross product etc.) for vectors #include "Vector3D.h" • The enumerated type defined below is used to distinguish between the two types of camera enum CAM_TYPE { LAND_CAM, AIR_CAM };
Camera Class Declaration • First, we need private variables for each view vector as well as the camera type and current position: class Camera { private: CAM_TYPE CameraType; Vector3D Position; Vector3D Along; Vector3D Up; Vector3D Forward; ...
Camera Class Declaration • Various construction/destruction, update and control functions are then declared publically: class Camera { ... public: Camera(CAM_TYPE ct = LAND_CAM); // Default: land virtual ~Camera(); void SetCameraType(CAM_TYPE ct); Vector3D GetPosition(); void Reset(); void Update(); ...
Camera Class Declaration • Finally, the motion and rotation functions are declared. • The boolean array, Wall[4], is an extra feature which modifies the motion of land cameras if they have to slide against walls (as opposed to going through them) class Camera { ... public: ... void Pitch(GLfloat theta); void Yaw(GLfloat theta); void Roll(GLfloat theta); void Walk(GLfloat delta, bool Wall[4]); void Strafe(GLfloat delta, bool Wall[4]); void Fly(GLfloat delta); };
Setup and Control Functions • The code listing on the following two slides is fairly self-explanatory • It comprises the basic constructor and destructor as well as a function to alter the camera type • The Reset() function sets the camera position to (0,0,0) and aligns the viewing axes with the local drawing coordinate system • Note that the default Forward vector points along the negative Z-axis
Setup and Control Functions Camera::Camera(CAM_TYPE ct) { SetCameraType(ct); Reset(); } Camera::~Camera() { } void Camera::SetCameraType(CAM_TYPEct) { CameraType = ct; }
Setup and Control Functions Vector3D Camera::GetPosition() { return Position; } void Camera::Reset() { Position = Vector3D(0.0, 0.0, 0.0); Along = Vector3D(1.0, 0.0, 0.0); Up = Vector3D(0.0, 1.0, 0.0); Forward = Vector3D(0.0, 0.0, -1.0); Update(); }
Building the View Matrix • The last function called by Reset() is probably the most important • The Update() function applies all changes made to the viewing axes and camera position, and updates the view in MODELVIEW mode • In actual fact, as with gluLookAt(), the perception of camera motion is achieved by moving the objects around the scene while keeping the camera at a fixed position • Instead of using translations and rotations, a view matrix may be built, meaning that just one OpenGL function call is needed – glLoadMatrix()
Building the View Matrix • First we obtain the camera virtual position coordinates using the dot product of pairs of the view vectors: void Camera::Update() { GLfloat x = DotProduct(Along, Position); GLfloat y = DotProduct(Up, Position); GLfloat z = DotProduct(Forward, Position); ... • These will be used to translate the camera (or rather, the scene) to its correct position
Building the View Matrix • The translation part of the view matrix is shown below: 1 0 0 0 T = 0 1 0 0 0 0 1 0 –x –y z 1 • Note that we must remember to make z positive, since for convenience we have taken “Forward” as meaning the direction into the screen which is opposite to OpenGL convention (Z-axis is positive outwards)
Building the View Matrix • The rotation part of the view matrix is built from the view vectors as shown: A.x U.x –F.x R = A.y U.y –F.y A.z U.z –F.z • Again, the Forward vector is reversed
Building the View Matrix • Combining these two matrices, we get: 1 0 0 0 A.x U.x –F.x 0 A.x U.x –F.x 0 V = 0 1 0 0 . A.y U.y –F.y 0 = A.y U.y –F.y 0 0 0 1 0 A.z U.z –F.z 0 A.z U.z –F.z 0 –x –y z 1 0 0 0 1 –x –y z 1 • The code on the following slides shows the rest of the implemented function
Building the View Matrix void Camera::Update() { ... Glfloat ViewMatrix[4][4]; ViewMatrix[0][0] = Along.x; ViewMatrix[0][1] = Up.x; ViewMatrix[0][2] = -Forward.x; ViewMatrix[0][3] = 0.0; ViewMatrix[1][0] = Along.y; ViewMatrix[1][1] = Up.y; ViewMatrix[1][2] = -Forward.y; ViewMatrix[1][3] = 0.0; ...
Building the View Matrix ... ViewMatrix[2][0] = Along.z; ViewMatrix[2][1] = Up.z; ViewMatrix[2][2] = -Forward.z; ViewMatrix[2][3] = 0.0; ViewMatrix[3][0] = -x; ViewMatrix[3][1] = -y; ViewMatrix[3][2] = z; ViewMatrix[3][3] = 1.0; glMatrixMode(GL_MODELVIEW); glLoadMatrixf((GLfloat *)&ViewMatrix); }
Camera Rotation Functions • The Pitch(), Yaw() and Roll() functions change the direction of the Forward, Along and Up vectors respectively • In each case, the rotation will result in the alteration of a second view vector, leaving one unchanged • The second modified vector is found by calculating the cross product of the other two vectors • This means that mutual orthognality is maintained for the three vectors
Camera Rotation Functions • Looking at a yaw from above, we can see how to calculate the new direction of the Along vector:
Camera Rotation Functions • Thus, for the Yaw function definition, we have: void Camera::Yaw(GLfloat theta) { Along = Along * cos(theta * DEG2RAD) + Forward * sin(theta * DEG2RAD); Along.Normalize(); Forward = CrossProduct(Along, Up) * -1.0; Update(); } • Pitch() and Roll() on the following slide look very similar
Camera Rotation Functions void Camera::Pitch(GLfloat theta) { // Invert UP/DOWN for air cameras if(CameraType == AIR_CAM) theta = -theta; Forward = Forward * cos(theta * DEG2RAD) + Up * sin(theta * DEG2RAD); Forward.Normalize(); Up = CrossProduct(Forward, Along) * -1.0; Update(); } void Camera::Roll(GLfloat theta) { if(CameraType == LAND_CAM) return; // Not for land cams Up = Up * cos(theta * DEG2RAD) - Along * sin(theta * DEG2RAD); Up.Normalize(); Along = CrossProduct(Forward, Up); Update(); }
Camera Motion Functions • Walk(), Strafe() and Fly() are a little easier to implement • In each case, all we have to do is add the correct scaled vector to the camera’s Position vector and update • As with rotation functions, motion functions work slightly differently depending on the type of camera being used • For example, when walking forward with a land camera, if the view has been pitched upwards, we do not want to move up the camera’s forward vector, but rather along a modified vector with the Y componet set to 0 – this will achieve the effect of staying on the ground rather than taking off into the air.
Camera Rotation Functions • The Walk function with wall handling also implemented: void Camera::Walk(GLfloat delta, bool Wall[4]) { if(CameraType == LAND_CAM) Position -= Vector3D(Forward.x * !(Wall[0] && Forward.x * delta > 0.0 || Wall[1] && Forward.x * delta < 0.0), 0.0, Forward.z * !(Wall[2] && Forward.z * delta > 0.0 || Wall[3] && Forward.z * delta < 0.0)) * delta; else Position -= Forward * delta; // Air camera Update(); }
Camera Rotation Functions • Similarly, the Strafe function is defined as follows: void Camera::Strafe(GLfloat delta, bool Wall[4]) { if(CameraType == LAND_CAM) Position -= Vector3D(Along.x * !(Wall[0] && Along.x * delta > 0.0 || Wall[1] && Along.x * delta < 0.0), 0.0, Along.z * !(Wall[2] && Along.z * delta > 0.0 || Wall[3] && Along.z * delta < 0.0)) * delta; else Position += Along * delta; // Air camera Update(); }
Camera Rotation Functions • Finally, flying is, of course, only allowed for air cameras: void Camera::Fly(GLfloat delta, bool Wall[4]) { // Don't allow for land cameras if(CameraType == LAND_CAM) return; Position += Up * delta; Update(); } • Although flying through walls has been allowed here, this would be implemented in the same manner as the previous two functions
References • Frank D. Luna, 2003, Introduction to 3D Game Programming with DirectX 9.0, Wordware Publishing, Inc. • Silicon Graphics Inc., 1997, OpenGL Programming Guide, Chapter 3 – Viewing, Addison-Wesley Publishing Company • Philipp Crocoll, The Advanced CodeColony Camera, http://www.codecolony.de/