Jump to content
  • Advertisement
Sign in to follow this  
danne89

OpenGL Camera

This topic is 4922 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello! I'm planing to implement a camera in a OpenGL program. But I can't get I right. I want to move where the camera is currently pointing. This's what I've done so fare. I think I must rotate the vector, which represent the way I want to move. I found a matrix which should do it in a book: [ cos(a) 0 -sin(a) 0 ] [ 0 1 0 0 ] [sin(a) 0 cos(a) 0 ] [ 0 0 0 1 ] Then I applicate it one my vector (x, y, z) and get a new one: (cos(a)*x - sin(a)*z, y, sin(a)*x + cos(a)*y) I really think I ought to post some code too. You must excuse that my OpenGL skills aren't exactly the best.
void draw()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    glTranslatef(0.0f, 0.0f, -6.0f);
    drawCube(0.0f, 0.0f);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0f, 640/480, 0.1f, 100.0f);

    glRotatef(playerAngle, 0.0f, 1.0f, 0.0f);
    
    GLfloat x,y,z,radians;
    radians = playerAngle * 0.017;
    x = cos(radians) * playerPosX - sin(radians) * playerPosZ;
    y = playerPosY;
    z = sin(radians) * playerPosX + cos(radians) * playerPosY;
    glTranslatef(x, y, z);

    glMatrixMode(GL_MODELVIEW);
    

    SDL_GL_SwapBuffers();
}


Share this post


Link to post
Share on other sites
Advertisement
First of all you have certain key fundamentals about openGL wrong.
Certain things have to be done in the right order, such as setting up the
projection matrix, clearing the buffer setting up the model matrix then
drawing, etc ...

Here's your some of your code, hopefully the right way around


void draw()
{
glMatrixMode(GL_PROJECTION); // Setting up the projection
glLoadIdentity(); // doesn't have to be done
gluPerspective(45.0f, 640/480, 0.1f, 100.0f); // each frame. Instead
// move it to a GLInit() procedure

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();



float radius = 6.0; // (New) The distance from the eye to the point of interest


glTranslatef(0.0f, 0.0f, -radius);
drawCube(0.0f, 0.0f);


glRotatef(playerAngle, 0.0f, 1.0f, 0.0f);

GLfloat x,y,z,radians;
radians = playerAngle * 0.017;
x = radius*cos(radians) * playerPosX - radius*sin(radians) * playerPosZ;
y = playerPosY;
z = radius*sin(radians) * playerPosX + radius*cos(radians) * playerPosY;
glTranslatef(x, y, z);


SDL_GL_SwapBuffers();
}



See if that works. Note the inclusion of a radius variable. Before you weren't multiplying
the cos and sin terms by the distance from the eye to the point of interest

Share this post


Link to post
Share on other sites
No, it isn't working. And I cannot understand the function of the radius variable. I also thought one should manipulate the projection matrix to get the camera effect. I'm, as I told you, quite new to this.

Edit: BTW, I do think I must set up the perspective in every frame, for I must call glIdentity and then one's perspective settings go away, right?

Share this post


Link to post
Share on other sites
I would break the camera up into its own class. It'll have it's own position, velocity, and rotational vectors, a function that will pass time (change position based on velocity, velocity based on friction), functions to move/rotate, and a function to apply the translations and rotations to the rest of the world (which will be called each frame).

You don't have to manipulate the projection matrix to get this to work. I find it much simpler to play with the modelview matrix, though I don't know if that copmlicates things if you have another player with its own camera (never done anything like that).

Also, what is it doing that you say it's not working?

Share this post


Link to post
Share on other sites
With the new code I cannot move at all! BTW, I don't see how I can get I work with only the modelview matrix.

Share this post


Link to post
Share on other sites
First of all, a camera system can be very hard to make if you're not familiar with some necesary math and your API (OpenGL).

I would recommend, to perhaps simplify things, that you use the function gluLookAt(). It will take 9 floats. The first three are your position, next three are the point which you look at and the last 3 is your up vector (The axis that's up, duh! [grin]). You will still need some math if you're trying to implement an FPS style camera, but at least you can boil it down to some simple cos/sin equations. Have you read about finding x and y force of a diagonal force, using cos and sin?
If you have, can you come up with a formula to make a point rotate about a center? Can you now make it so a point in a 3D space can rotate about a center? If you've reached this far, you should have little trouble figuring out how to use that knowledge with gluLookAt() (hint: look at point rotates about camera center...).

Another way, the one you're after, is by using matrices. That requires a lot more math since you have to do all translations/rotation/concatenations/inverses/... ( ..i don't what there is to it [grin]) that gluLookAt() will do for you.

When you've studied some more matrix math, perhaps then you can write a camera system that works entierly out of matrices, but take it easy at the start [smile]

Hope it helps!

Share this post


Link to post
Share on other sites
This is my 3rd person camera system. Hopefully this should get you started
in understanding how to implement a camera (i'm sure some people will also
tell you it not how to implement a camera).

There are a number of files place the code into right file and the camera
should be ready to use.


/*

A simple 3rd person camera class

Basic usage:

void Init()
{
// Set up defaults:

CVector pointOfInterest(0,0,0);
float radius = 6.0; // distance between camera and point of interest
float elevation = 45;
float yaw = 45;
camera->SetDefaults(pointOfInterest, radius, yaw, elevation);
}

void DrawScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();

camera->focalPoint.Set(0,0,0); // the point of interest where the camera revolves around
camera->LoadMatrix();
}

void DoKeyLeft()
{
camera->IncYaw(-1.0); // decrease yaw by 1 degree
}

void DoKeyUp()
{
camera->IncElev(-1.0); // decrease elev by 1 degree
}


*/



//---------------------------------------------------------------------------
// Camera.h
//---------------------------------------------------------------------------

#ifndef GameX_Camera_H
#define GameX_Camera_H

#include "CVector.h"

// A pretty Simple camera 3rd person camera
//
// Focal point == the point of interest. i.e. the point that
// the camera will revolve around


class Camera
{
public:
Camera(CVector origin0, float R0, float Elev0, float Yaw0);
virtual ~Camera();

void LoadMatrix();
void Reset();
void SetDefaults(CVector origin0, float R0, float Elev0, float Yaw0);
const float& Elev() {return elev; }
const float& Yaw() {return yaw; }
void IncR(float dr);
void IncElev(float change);
void IncYaw(float change);
void SetR(float r);
void SetYaw(float yaw);
void SetElev(float elev);

public:
CVector focalPoint;

protected:
float elev;
float yaw;
float r;
float r0, elev0, yaw0;
CVector origin0;
};

#endif


//---------------------------------------------------------------------------
// Camera.cpp
//---------------------------------------------------------------------------
#include <windows.h>
#include <gl/gl.h>

#include "Camera.h"

Camera::Camera(CVector Origin0, float R0, float Elev0, float Yaw0)
{
SetDefaults(Origin0, R0, Elev0, Yaw0);
}

Camera::~Camera()
{
}

void
Camera::SetDefaults(CVector Origin0, float R0, float Elev0, float Yaw0)
{
origin0 = Origin0;
elev0 = Elev0;
yaw0 = Yaw0;
r0 = R0; // Distance from focalPoint
Reset();
}


void
Camera::Reset()
{
elev = elev0;
yaw = yaw0;
r = r0;
focalPoint = origin0;
}

void
Camera::LoadMatrix()
{
// Set up the axis to that
// Z is up
// Y is forward
// Z is right
glRotatef(-90,1,0,0);

glTranslatef(0, r , 0);
glRotatef(elev, 1,0,0);
glRotatef(-yaw, 0,0,1);

glTranslatef(-focalPoint.x, -focalPoint.y, -focalPoint.z);
}

void
Camera::IncR(float dr)
{
SetR(r+dr);
}
void
Camera::IncElev(float change)
{
SetElev(elev + change);
}

void
Camera::IncYaw(float change)
{
SetYaw(yaw+change);
}

void
Camera::SetR(float r_)
{
r = r_;
if (r < 1) r = 1;
if (r > 50) r = 50;
}

void
Camera::SetYaw(float yaw_)
{
yaw = yaw_;
if (yaw >= 180.0) yaw -= 360;
if (yaw < -180.0) yaw += 360;
}

void
Camera::SetElev(float elev_)
{
elev = elev_;
if (elev > 90) elev = 90;
if (elev < -90) elev = -90;
}


//---------------------------------------------------------------------------
// CVector.h
//---------------------------------------------------------------------------

#ifndef CVECTOR_H
#define CVECTOR_H

#include "mem.h" // for memset
#include "math.h"

class CVector;
class CMatrix;

class CVector
{
public:
union
{
struct
{
float x;
float y;
float z;
float w;
};
float v[4];
};

inline CVector() {}
inline CVector(const float &xx, const float &yy, const float &zz, const float &ww = 1);

inline void Set(const float &xx, const float &yy, const float &zz, const float &ww = 1);
inline void Set(const float *pv);
inline CVector& operator = (const CVector &v);

inline CVector& operator += (const CVector &v);
inline CVector& operator -= (const CVector &v);

inline CVector operator + (const CVector &v) const;
inline CVector operator - (const CVector &v) const;

inline void Scale(const float &a);
inline CVector operator * (const float &a) const;
inline CVector operator / (const float &a) const;
inline void operator *= (const float &a);
inline void operator /= (const float &a);

inline float Dot(const CVector &a) const;
inline CVector Cross (const CVector &a) const;

inline float Modulus() const;
inline CVector UnitVec() const;
inline void Normalise();
inline void SetLength(const float &len);

inline void RotateX(float ang);
inline void RotateY(float ang);
inline void RotateZ(float ang);
inline void Rotate(float theta, CVector v);

inline void Lerp(const CVector &v1, const CVector &v2, float k);

// Cast to float
inline operator float*() { return v; }

private:
friend class CMatrix;

};

/*
===============================================================================
Class: CMatrix
===============================================================================
*/


class CMatrix
{
public:
union
{
float m[16];
struct
{
float m11, m21, m31, m41; // Stored like opengl matrix
float m12, m22, m32, m42; // Indices are (row, column)
float m13, m23, m33, m43;
float m14, m24, m34, m44;
};
};

inline CMatrix();
inline void LoadIdentity();
CVector operator * (const CVector &v) const;
CMatrix operator * (const CMatrix &In);

inline void GetBasis(CVector &X, CVector &Y, CVector &Z) const;
inline void GetInverseBasis(CVector &X, CVector &Y, CVector &Z) const;

inline void SetBasis(CVector &X, CVector &Y, CVector &Z);
inline void SetInverseBasis(CVector &X, CVector &Y, CVector &Z);

inline void Transpose();

private:
friend class CVector;

};


// Define CVertex to the be the same as CVector

typedef class CVector CVertex;


#include "CVector.inl"

#endif // CVECTOR_H

//---------------------------------------------------------------------------
// CVector.inl
//---------------------------------------------------------------------------

/*
===============================================================================
Inlines for Class: CVector
===============================================================================
*/


inline
CVector::CVector(const float &xx, const float &yy, const float &zz, const float &ww)
{
x = xx;
y = yy;
z = zz;
w = ww;
}

inline
void
CVector::Set(const float &xx, const float &yy, const float &zz, const float &ww)
{
x = xx;
y = yy;
z = zz;
}

inline
void
CVector::Set(const float *pv)
{
x = pv[0];
y = pv[1];
z = pv[2];
}

inline
CVector& CVector::operator = (const CVector &v)
{
x = v.x;
y = v.y;
z = v.z;
return *this;
}


inline
CVector&
CVector::operator += (const CVector &v)
{
x += v.x;
y += v.y;
z += v.z;
return *this;
}

inline
CVector&
CVector::operator -= (const CVector &v)
{
x -= v.x;
y -= v.y;
z -= v.z;
return *this;
}

inline
CVector
CVector::operator + (const CVector &v) const
{
return CVector(x + v.x, y + v.y, z + v.z);
}

inline
CVector
CVector::operator - (const CVector &v) const
{
return CVector(x - v.x, y - v.y, z - v.z);
}


inline
void
CVector::Scale(const float &a)
{
x *= a;
y *= a;
z *= a;
}

inline
CVector
CVector::operator * (const float &a) const // Scale
{
return CVector (x*a, y*a, z*a);
}

inline
CVector
CVector::operator / (const float &a) const
{
return CVector(x/a, y/a, z/a);
}

inline
void
CVector::operator *= (const float &a)
{
x *= a;
y *= a;
z *= a;
}

inline
void
CVector::operator /= (const float &a)
{
x /= a;
y /= a;
z /= a;
}

inline
float
CVector::Dot(const CVector &a) const
{
return (a.x * x) + (a.y * y) + (a.z * z);
}

inline
CVector
CVector::Cross (const CVector &a) const
{
return CVector(y*a.z - z*a.y,
z*a.x - x*a.z,
x*a.y - y*a.x);
}

inline
float
CVector::Modulus() const
{
return sqrt(x*x + y*y + z*z);
}

inline
CVector
CVector::UnitVec() const
{
float R = Modulus();
if (R == 0.0)
{
return CVector(0,0,0);
}
else
{
return CVector(x/R, y/R, z/R);
}
}

inline
void
CVector:: Normalise()
{
float R = Modulus();
if (R != 0.0)
{
x /= R;
y /= R;
z /= R;
}
}

inline
void
CVector::SetLength(const float &len)
{
Normalise();
x *= len;
y *= len;
z *= len;
}

inline
void
CVector::RotateZ(float ang)
{
float COS = cos(ang);
float SIN = sin(ang);

float oldx = x;
x = oldx*COS - y*SIN;
y = y*COS + oldx*SIN;
}

inline
void
CVector::RotateX(float ang)
{
float COS = cos(ang);
float SIN = sin(ang);

float oldy = y;
y = oldy*COS - z*SIN;
z = z*COS + oldy*SIN;
}

inline
void
CVector::RotateY(float ang)
{
float COS = cos(ang);
float SIN = sin(ang);

float oldz = z;
z = oldz*COS - x*SIN;
x = x*COS + oldz*SIN;
}

inline
void
CVector::Rotate(float theta, CVector v)
{
v.Normalise();
float COS = cos(theta);

// Borrowed this formula from Diana Gruber, FastGraph
// (but needs optimising to reduce constructor calls!)

*this = (*this)*COS + v*((v.Dot(*this))*(1-COS)) - (this->Cross(v))*sin(theta);
}

inline
void
CVector::Lerp(const CVector &v1, const CVector &v2, float t)
{
float s = 1-t;
x = v1.x*s + v2.x*t;
y = v1.y*s + v2.y*t;
z = v1.z*s + v2.z*t;
}

/*
===============================================================================
Inlines for Class: CMatrix
===============================================================================
*/


inline
CMatrix::CMatrix()
{
LoadIdentity();
}

inline
void
CMatrix::LoadIdentity()
{
memset(this,0,sizeof(m));
m11 = 1.0;
m22 = 1.0;
m33 = 1.0;
m44 = 1.0;
}

inline
CVector
CMatrix::operator * (const CVector &v) const
{
// Should probably change this to a for-loop

CVector Out;

Out.x = (m11 * v.x) + (m12 * v.y) + (m13 * v.z) + m14;
Out.y = (m21 * v.x) + (m22 * v.y) + (m23 * v.z) + m24;
Out.z = (m31 * v.x) + (m32 * v.y) + (m33 * v.z) + m34;
Out.w = 1;

return Out;
}

inline
CMatrix
CMatrix::operator * (const CMatrix &In)
{
// Should probably change this to a for-loop

CMatrix Out;

Out.m11 = (m11 * In.m11) + (m12 * In.m21) + (m13 * In.m31) + (m14 * In.m41);
Out.m21 = (m21 * In.m11) + (m22 * In.m21) + (m23 * In.m31) + (m24 * In.m41);
Out.m31 = (m31 * In.m11) + (m32 * In.m21) + (m33 * In.m31) + (m34 * In.m41);
Out.m41 = 0;

Out.m12 = (m11 * In.m12) + (m12 * In.m22) + (m13 * In.m32) + (m14 * In.m42);
Out.m22 = (m21 * In.m12) + (m22 * In.m22) + (m23 * In.m32) + (m24 * In.m42);
Out.m32 = (m31 * In.m12) + (m32 * In.m22) + (m33 * In.m32) + (m34 * In.m42);
Out.m42 = 0;

Out.m13 = (m11 * In.m13) + (m12 * In.m23) + (m13 * In.m33) + (m14 * In.m43);
Out.m23 = (m21 * In.m13) + (m22 * In.m23) + (m23 * In.m33) + (m24 * In.m43);
Out.m33 = (m31 * In.m13) + (m32 * In.m23) + (m33 * In.m33) + (m34 * In.m43);
Out.m43 = 0;

Out.m14 = (m11 * In.m14) + (m12 * In.m24) + (m13 * In.m34) + (m14 * In.m44);
Out.m24 = (m21 * In.m14) + (m22 * In.m24) + (m23 * In.m34) + (m24 * In.m44);
Out.m34 = (m31 * In.m14) + (m32 * In.m24) + (m33 * In.m34) + (m34 * In.m44);
Out.m44 = 1;

return Out;
}

inline
void
CMatrix::GetBasis(CVector &X, CVector &Y, CVector &Z) const
{
X.Set(m11, m12, m13);
Y.Set(m21, m22, m23);
Z.Set(m31, m32, m33);
}

inline
void
CMatrix::GetInverseBasis(CVector &X, CVector &Y, CVector &Z) const
{
X.Set(m11, m21, m31);
Y.Set(m12, m22, m32);
Z.Set(m13, m23, m33);
}

inline
void
CMatrix::SetBasis(CVector &X, CVector &Y, CVector &Z)
{
m11 = X.x; m12 = X.y; m13 = X.z;
m21 = Y.x; m22 = Y.y; m23 = Y.z;
m31 = Z.x; m32 = Z.y; m33 = Z.z;
}

inline
void
CMatrix::SetInverseBasis(CVector &X, CVector &Y, CVector &Z)
{
m11 = X.x; m21 = X.y; m31 = X.z;
m12 = Y.x; m22 = Y.y; m32 = Y.z;
m13 = Z.x; m23 = Z.y; m33 = Z.z;
}

#define SWAP(a, b, type) {type tmp = a; a = b; b = tmp;}

inline
void
CMatrix::Transpose()
{
SWAP(m12,m21, float);
SWAP(m13,m31, float);
SWAP(m14,m41, float);
SWAP(m23,m32, float);
SWAP(m24,m42, float);
SWAP(m34,m43, float);
}


Share this post


Link to post
Share on other sites
Hmm. Scary indeed.
Can you check if I get the diagonal force thingie right, please?

void Camera::Move(GLfloat x, GLfloat y, GLfloat z)
{
posX += cos(0.017f * angle) * x - sin(0.017f * angle) * z;
posY += y;
posZ += sin(0.017f * angle) * x + cos(0.017f * angle) * y;
}




EDIT: Maybe worth saying: this is intend to be yaw-rotation.

Share this post


Link to post
Share on other sites
Heh, what a pain it is when you realize you have forgot basic math, and need to re-learn it [grin]

Well, your formula looks almost correct. There should be a Z on the end of the last row, but i bet you knew that already.

What i can't figure out is how you have your camera set up. Using these formulas you will rotate a vector around an axis based from it's last point.

Quote:

void Camera::Move(GLfloat x, GLfloat y, GLfloat z)
{
posX += cos(0.017f * angle) * x - sin(0.017f * angle) * z;
posY += y;
posZ += sin(0.017f * angle) * x + cos(0.017f * angle) * y;
}


I guess i just get stuck with you calling the function move since that's not at all what you do...you rotate your LookAt point, for example...

I would rather do something like this [smile]

// this is what you call once you rotate the camera
void Camera::OnRotate()
{
this->rot += 5.0f;
}

// You need a function to move, ignore the math ;)
void Camera::OnMove()
{
this->moveVec.x = this->view.x;
this->moveVec.z = this->view.z;
}

// This should be called every frame, or every time the camera needs to be updated
void Camera::Update()
{
if(this->moveVec.x != 0 || this->moveVec.z != 0)
{
this->pos.x += moveVec.x;
this->pos.z += moveVec.z;
}

if(this->rot != 0)
{
const float cosTheta = cos(this->rot);
const float sinTheta = sin(this->rot);
this->view.x = cosTheta * x - sinTheta * z;
this->view.z = sinTheta * x + cosTheta * z;
this->rot = 0;
}
}

// and last, something that "draw" or place the camera
void Camera::Draw()
{
gluLookAt(this->pos.x, this->pos.y, this->pos.z,
this->pos.x + this->view.x, this->pos.y + this->view.z, this->pos.z + this->view.z,
0, 1, 0);
}




Don't trust that code, i didn't think much when writing it, and it hasn't been tested, but it should be enough to give you something to work with. I havn't bothered about the up-vector since you only do yaw rotations, but it's really easy to add in.

If you want to read further about the formulas used to rotate, they're called addition and subtraction formulas for cosine and sinus...or something like that. [smile]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!