Anda di halaman 1dari 33

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 cameras own orientation by keeping the viewing vectors orthogonal to each other Land camera e.g. for road vehicles simulation Air camera e.g. for flight simulation

Need to define motion for two possible types of camera:


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):

Camera Rotation

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 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 Vector3D Vector3D Vector3D Vector3D ... { CameraType; Position; Along; Up; 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 selfexplanatory 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 x 0 1 0 y 0 0 1 z 0 0 0 1

T=

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 A.y U.y F.y A.z U.z F.z

R=

Again, the Forward vector is reversed

Building the View Matrix

Combining these two matrices, we get:


1 0 0 x 0 1 0 y 0 0 1 z 0 0 . 0 1 A.x U.x F.x 0 A.y U.y F.y 0 A.z U.z F.z 0 0 0 0 1 A.x U.x F.x 0 A.y U.y F.y 0 A.z U.z F.z 0 x y z 1

V=

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] ViewMatrix[1][1] ViewMatrix[1][2] ViewMatrix[1][3] ... = = = = Along.y; Up.y; -Forward.y; 0.0;

Building the View Matrix


... ViewMatrix[2][0] ViewMatrix[2][1] ViewMatrix[2][2] ViewMatrix[2][3] ViewMatrix[3][0] ViewMatrix[3][1] ViewMatrix[3][2] ViewMatrix[3][3] = = = = = = = = Along.z; Up.z; -Forward.z; 0.0; -x; -y; z; 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 cameras 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 cameras 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/