# OpenGL Quaternion Camera Implementation

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

## Recommended Posts

I'm trying to get a quaternion camera working but i'm struggling a bit, strangely enough not with the maths behind the quaternion but rather how to implement it in opengl. The tutorials i've found all use different methods. Some directly modify the modelview matrix while others make using of transformations via glRotate/glTranslate's and finally some use the gluLookAt() function (i know this is a shortcut to the transforms). My quaternion maths is right and my class can produce anything needed for any of the above methods. can you give me a brief overview of where i need to start and how to implement the camera? like should i use heading and pitch variables (kinda like euler angles), should i use a heading vector and work from that? Whats the difference between these methods? Is there a prefered solution? here are my vector, quaternion and camera classes, i've implemented a basic rotate method (from the NEHE tutorial) which doesnt work as intended, it seems to rotate spirally ?!? Point and Vector Classes :
class point3f
{
public:

//members
//------------------------------------------------------------------------------

float x, y, z;

//constructors
//------------------------------------------------------------------------------

point3f() : x(0),y(0),z(0) {}
point3f(float ix, float iy, float iz) : x(ix), y(iy), z(iz) {}

//methods
//------------------------------------------------------------------------------

point3f operator +(point3f p)
{
return point3f(x + p.x, y + p.y, z + p.z);
}

//subtraction
point3f operator -(point3f p)
{
return point3f(x -p.x, y - p.y, z - p.z);
}

//convert to array
void toArray(float* a)
{
a[0] = x;
a[1] = y;
a[2] = z;
}
};

//vector class
class vector3f
{

public:

//members
//------------------------------------------------------------------------------

float x, y, z;

//constructors
//------------------------------------------------------------------------------

//default constructor
vector3f() : x(0),y(0),z(0) {}

//create vector from point
vector3f(point3f p) : x(p.x), y(p.y), z(p.z) {}

vector3f operator =(point3f p)
{
return vector3f(p);
}

//create vector with co-ordinates
vector3f(float ix, float iy, float iz) : x(ix), y(iy), z(iz) {}

//methods
//------------------------------------------------------------------------------

//cross product
vector3f crossProduct(const vector3f &v)
{
vector3f cp;

//cross product
cp.x =	y * v.z - z * v.y;
cp.y =	-( x * v.z - z * v.x );
cp.z =	x * v.y - y * v.x;

return cp;
}

//normalize
void normalize()
{
float sumSquares = x*x + y*y + z*z;

//dont normalise if vector is close enough
if ( abs(sumSquares - 1.0) > VECTOR_TOLERANCE )
{
float L = sqrt(sumSquares);

x /= L;
y /= L;
z /= L;
}
}

//return normalize copy of current vector
vector3f getNormalized()
{
//copy current quarternion and return conjugate
vector3f v(*this);
v.normalize();

return v;
}

//dot product
float dotProduct(const vector3f &v)
{
return x * v.x + y * v.y + z * v.z;
}

//shortcut for cross product
vector3f operator *(vector3f &v)
{
return crossProduct(v);
}

vector3f operator +(vector3f &v)
{
return vector3f(x + v.x, y + v.y, z + v.z);
}

//subtraction
vector3f operator -(vector3f &v)
{
return vector3f(x - v.x, y - v.y, z - v.z);
}
};

Quaternion Class:
class quaternion
{
public:

//members
//------------------------------------------------------------------------------

float x,y,z,w;

public:

//constructors
//------------------------------------------------------------------------------

//empty constructor
quaternion() : x(0), y(0), z(0), w(0) {}

//specific constructor
quaternion(float xi, float yi, float zi, float wi) : x(xi), y(yi), z(zi), w(wi) {}

//from vector
quaternion(vector3f v) : x(v.x), y(v.y), z(v.z), w(0) {}

//create from an angle and an arbitrary axis
quaternion(float degrees, vector3f axis)
{
float theta = (degrees / 180.0f) * PI;
float sinTheta = sin( theta / 2.0f );
float cosTheta = sin( theta / 2.0f );

//calculate quaternion values
x = axis.x * sinTheta;
y = axis.y * sinTheta;
z = axis.z * sinTheta;
w = cosTheta;
}

//methods
//------------------------------------------------------------------------------

//define quaternion multiplication operator
quaternion operator *(const quaternion &b)
{
quaternion result;

result.x = w * b.x + x * b.w + y * b.z - z * b.y;
result.y = w * b.y - x * b.z + y * b.w + z * b.x;
result.z = w * b.z + x * b.y - y * b.x + z * b.w;
result.w = w * b.w - x * b.x - y * b.y - z * b.z;

//return new quaterion
return(result);
}

//define vector multiplication operator
vector3f operator *(const vector3f &vi)
{
//create quaternion from normalized vector
vector3f vn(vi);
vn.normalize();
quaternion v(vn), result;

//apply multiplications
result = v * getConjugate();
result = *this * result;

return vector3f(result.x, result.y, result.z);
}

//normalise quaterion
void normalize()
{
float sumSquares = x * x + y * y + z * z + w * w;

if ( abs(sumSquares - 1.0) > QUATERNION_TOLERANCE )
{
float L = sqrt(sumSquares);

x /= L;
y /= L;
z /= L;
w /= L;
}
}

//return normalized copy of current quaternion
quaternion getNormalized()
{
//copy current quarternion and return conjugate
quaternion q(*this);
q.normalize();

return q;
}

//conjugate quaterion
void conjugate()
{
x = -x;
y = -y;
z = -z;
}

//return conjugate copy of current quaternion
quaternion getConjugate()
{
//copy current quarternion and return conjugate
quaternion q(*this);
q.conjugate();

return q;
}

//to vector
vector3f toVector()
{
return vector3f(x, y, z);
}

//to matrix (homogenous 4x4 matrix)
void toMatrix(float* matrix)
{
// First row
matrix[0]	= 1.0f - 2.0f * ( y * y + z * z );
matrix[1]	= 2.0f * (x * y + z * w);
matrix[2]	= 2.0f * (x * z - y * w);
matrix[3]	= 0.0f;

// Second row
matrix[4]	= 2.0f * ( x * y - z * w );
matrix[5]	= 1.0f - 2.0f * ( x * x + z * z );
matrix[6]	= 2.0f * (z * y + x * w );
matrix[7]	= 0.0f;

// Third row
matrix[8]	= 2.0f * ( x * z + y * w );
matrix[9]	= 2.0f * ( y * z - x * w );
matrix[10]	= 1.0f - 2.0f * ( x * x + y * y );
matrix[11]	= 0.0f;

// Fourth row
matrix[12]	= 0;
matrix[13]	= 0;
matrix[14]	= 0;
matrix[15]	= 1.0f;
}

};

Camera Class:
class camera
{
public:

//constructors
//------------------------------------------------------------------------------

camera(vector3f p, vector3f v, vector3f u) : position(p), view(v), up(u) {}

//methods
//------------------------------------------------------------------------------

void rotate(float h, float p)
{
GLfloat m[16];

// Make the Quaternions that will represent our rotations
quaternion qh(h,vector3f(1,0,0));
quaternion qp(p,vector3f(0,1,0));

// Combine the pitch and heading rotations and store the results in q
quaternion result = qp * qh;
result.toMatrix(m);

// Let OpenGL set our new prespective on the world!
glMultMatrixf(m);
}

void changePosition()
{

}
};

[Edited by - Coldon on October 17, 2007 3:32:14 AM]

##### Share on other sites
use Cos if you write Cos ;)

float cosTheta = sin( theta / 2.0f ); // SHOULD BE COS

I don't think that's your only problem here :/ your methods look well implemented, though. could you describe the artifacts?

##### Share on other sites
again it's me.

I never implemented a quat-based camera, but like the trackball it can be imagined as the task to rotate one starting point P to a target point P' on the unit sphere. to compute the quaternion you are looking for, you have to
a) compute the rotation axis (P.cross(P'))
b) the rotation angle (acos(P.dot(P'))

you can then use your constructor "quaternion(float degrees, vector3f axis)" to build the rotation Q that moves P into P'. last step should be to convert Q into a 4x4 matrix (I use glLoadMatrix()).

I don't know if it's necessary for a camera-implementation, but for the trackball you have to store the last computed quaternion as starting point for the next rotation...but not sure here.

greetx,
me.

##### Share on other sites
hey, lol - that was a dumb mistake with the cos function, dangers of cutting and pasting. I'll try what you suggest and get back to you tomorrow.

##### Share on other sites
I have a quaternion camera implementation in opengl and direct3d over on my site that might be of help to you.

##### Share on other sites
@dpoon:

your camera examples are excellent! I really like your coding style as its really similar to my own and its neat clear and concise!

That stupid typo was causing the problems, once i fixed it the quaternion rotations work as intended, i'm busy getting movement working but its proving to be a little tricky but i'll get it (eventually)...

1. 1
2. 2
3. 3
Rutin
17
4. 4
5. 5

• 14
• 9
• 9
• 9
• 10
• ### Forum Statistics

• Total Topics
632912
• Total Posts
3009185
• ### Who's Online (See full list)

There are no registered users currently online

×