Jump to content
  • Advertisement
Sign in to follow this  
gcmonk

OpenGL camera using quaternion

This topic is 4860 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

I'm trying to implement a camera class in OpenGL. I'm not sure if it's rounding errors or I implemented quaternions wrong. Here are some screenshots of what happens. I'm changing the rotation in the x axis by 0.5 degrees. The first pic is where I start. In the second pic I rotate by 0.5 degrees in the x axis and change the view vector. In the third pic I go rotate back 0.5 degrees in the x axis and change the view vector. I use my implementation of quaternions to rotate the view vector. As you can see on the top left of the screen, I don't get the same values back from the first pic. Heres some relevant code: quaternion.h
#ifndef GQUATERNION_H
#define GQUATERNION_H

#include "GVector.h"

//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
class GQuaternion {
public:
  GQuaternion() { Init(0.0f, GVector(0.0f, 0.0f, 0.0f)); }
  GQuaternion(float aW, GVector aV) { Init(aW, aV); }
  GQuaternion(float aW, float aX, float aY, float aZ) { Init(aW, aX, aY, aZ); }
  ~GQuaternion();
  float Length() const { return sqrt(w()*w() + x()*x() + y()*y() + z()*z()); }
  GQuaternion Normalize() { float l = Length(); return GQuaternion(w()/l, x()/l, y()/l, z()/l); }
  GQuaternion Conjugate() { return GQuaternion(mW, -mV); }
  GQuaternion& operator*=(const GQuaternion &mQ);
  GQuaternion operator*(const GQuaternion &mQ) { return GQuaternion(*this) *= mQ; }
  static GQuaternion FromAxisAngle(float aAngle, GVector aAxis);
  static void ToAxisAngle(float *aAngle, float aVector);
  float w() const { return mW; }
  float x() const { return mV.x(); }
  float y() const { return mV.y(); }
  float z() const { return mV.z(); }
  void SetW(float aW) { mW = aW; }
  void SetX(float aX) { mV.v[_X] = aX; }
  void SetY(float aY) { mV.v[_Y] = aY; }
  void SetZ(float aZ) { mV.v[_Z] = aZ; }
  //void RotateX(float aAngle, const GVector &mV);
  void Rotate(float aAngle, const GVector &mV);
protected:
  void Init(float aW, GVector aV) { mW = aW; mV = aV; }
  void Init(float aW, float aX, float aY, float aZ) { mW = aW; mV.Set(aX, aY, aZ); }
protected:
  float mW;
  GVector mV;
};
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------

#endif

quaternion.cpp
#include "defines.h"
#include "GQuaternion.h"

//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
GQuaternion::~GQuaternion() {
}
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
GQuaternion& GQuaternion::operator*=(const GQuaternion &aQ) {    
  mV.v[_X] = w()*aQ.x() + x()*aQ.w() + y()*aQ.z() - z()*aQ.y();
  mV.v[_Y] = w()*aQ.y() - x()*aQ.z() + y()*aQ.w() + z()*aQ.x();
  mV.v[_Z] = w()*aQ.z() + x()*aQ.y() - y()*aQ.x() + z()*aQ.w();
        mW = w()*aQ.w() - x()*aQ.x() - y()*aQ.y() - z()*aQ.z();
  return *this;
}
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
GQuaternion GQuaternion::FromAxisAngle(float aAngle, GVector aAxis) {
  return GQuaternion(
    COS(aAngle/2.0f),
    aAxis.x() * SIN(aAngle/2.0f),
    aAxis.y() * SIN(aAngle/2.0f),
    aAxis.z() * SIN(aAngle/2.0f)
  );
}
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
void GQuaternion::ToAxisAngle(float *aAngle, float aVector) {

}
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
void GQuaternion::Rotate(float aAngle, const GVector &aVector) {
}
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------

camera.h
#ifndef GCAMERA_H
#define GCAMERA_H

#include "GPoint.h"
#include "GVector.h"

class GCamera {
public:
  GCamera();
  ~GCamera();

  void Run();
  void MoveForward();
  void MoveBackward();
  float GetPosX() { return mPosition.x(); }
  float GetPosY() { return mPosition.y(); }
  float GetPosZ() { return mPosition.z(); }
  float GetAngleX() { return mAngleX; }
  float GetAngleY() { return mAngleY; }
  float GetAngleZ() { return mAngleZ; }
  void AddAngleX(float aAngle);
  void AddAngleY(float aAngle);
  void AddAngleZ(float aAngle);

  float GetViewX() { return mView.x(); }
  float GetViewY() { return mView.y(); }
  float GetViewZ() { return mView.z(); }  
  //float GetDirX() { return mDirection.x(); }
  //float GetDirY() { return mDirection.y(); }
  //float GetDirZ() { return mDirection.z(); }

  void RotateCamera(float aAngle, GVector aAxis);
  void RotateCamera();

  void Reset() { mPosition.Set(0.0f, 0.0f, 0.0f); mView.Set(0.0f, 0.0f, -1.0f); mAngleX = mAngleY = mAngleZ = 0.0f; }
  
protected:
  GVector mPosition;
  GVector mView;
  GVector mUp;
  GVector mRight;
  float mAngleX;
  float mAngleY;
  float mAngleZ;
  float mAngleDX;
  float mAngleDY;
  float mAngleDZ;
  float mVelocity;
};

#endif

camera.cpp
//#include <gl/gl.h>
//#include <gl/glu.h>
//
#include "GQuaternion.h"
#include "GCamera.h"

GCamera::GCamera() {
 
  mAngleDX = mAngleX = 0.0f;
  mAngleDY = mAngleY = 0.0f;
  mAngleDZ = mAngleZ = 0.0f;
  mVelocity = 0.0f;
  mPosition.Set(0.0f, -10.0f,0.0f);
  mView.Set(0.0f, 0.0f, -1.0f);
  mUp.Set(0.0f, 1.0f, 0.0f);
  mRight.Set(1.0f, 0.0f, 0.0f);
  
  mView.RotateX(mAngleX);
}

GCamera::~GCamera() {
}

void GCamera::Run() {  
}

void GCamera::MoveForward() {
  mPosition -= mView;  
}

void GCamera::MoveBackward() {
  mPosition += mView;
}

void GCamera::AddAngleX(float aAngle) {
  mAngleX += aAngle;
  if(mAngleX > 360.0f) mAngleX -= 360.0f;
  if(mAngleX < 0.0f) mAngleX += 360.0f;
  mAngleDX = aAngle;
}
void GCamera::AddAngleY(float aAngle) {
  mAngleY += aAngle;
  if(mAngleY > 360.0f) mAngleY -= 360.0f;
  if(mAngleY < 0.0f) mAngleY += 360.0f;
  mAngleDY = aAngle; 
}
void GCamera::AddAngleZ(float aAngle) {
  mAngleZ += aAngle;
  if(mAngleZ > 360.0f) mAngleZ -= 360.0f;
  if(mAngleZ < 0.0f) mAngleZ += 360.0f;
  mAngleDZ = aAngle; 
}

void GCamera::RotateCamera() {
  if(mAngleDX != 0) 
    RotateCamera(mAngleDX, mRight);
  mAngleDX = 0;
}


void GCamera::RotateCamera(float aAngle, GVector aAxis) {
  //if(aAngle == 0.0f) return;
  
  GQuaternion temp;
  GQuaternion view;

  aAxis.Normalize();
  
  temp.SetX(aAxis.x() * SIN(aAngle/2.0));
  temp.SetY(aAxis.y() * SIN(aAngle/2.0));
  temp.SetZ(aAxis.z() * SIN(aAngle/2.0));
  temp.SetW(COS(aAngle/2.0));
  temp.Normalize();
  
  view.SetX(mView.x());
  view.SetY(mView.y());
  view.SetZ(mView.z());
  view.SetW(0.0);
  view.Normalize();

  GQuaternion result = (temp.Conjugate()) * (temp * view);
  result.Normalize();
  mView.Set(result.x(), result.y(), result.z());
  mView.Normalize();
  //mView.Normalize();
  //mView.Normalize();
}


somewhere in my main loop i have this
  glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
  glLoadIdentity();
  glPushMatrix();        
    glRotatef(mCamera->GetAngleX(), 1.0f, 0.0f, 0.0f);
    glRotatef(mCamera->GetAngleY(), 0.0f, 1.0f, 0.0f);
    glRotatef(mCamera->GetAngleZ(), 0.0f, 0.0f, 1.0f);    
    glTranslatef(mCamera->GetPosX(), mCamera->GetPosY(), mCamera->GetPosZ());
    mMap->Render();    
  glPopMatrix();
  gGraphics->Enable2D();
  char buf[300];
  sprintf(buf, "position => (%f, %f, %f)", mCamera->GetPosX(), mCamera->GetPosY(), mCamera->GetPosZ());
  gGraphics->OutString(10, gHeight-20, buf); 
  sprintf(buf, "angle    => (%f, %f, %f)", mCamera->GetAngleX(), mCamera->GetAngleY(), mCamera->GetAngleZ());
  gGraphics->OutString(10, gHeight-40, buf); 
  sprintf(buf, "view => (%f, %f, %f)", mCamera->GetViewX(), mCamera->GetViewY(), mCamera->GetViewZ());
  gGraphics->OutString(10, gHeight-60, buf);
  glRasterPos2i(0,0);
  glDrawPixels(gGame->GetBitmap(mBitmap)->GetWidth(), gGame->GetBitmap(mBitmap)->GetHeight(), GL_RGB, GL_UNSIGNED_BYTE, gGame->GetBitmap(mBitmap)->mBitmapData);
  gGraphics->Disable2D();

  // input
  if(gInput->IsKeyPressed(VK_ESCAPE)) gGame->Exit();

  if(gInput->IsKeyPressed('W')) 
    mCamera->AddAngleX(0.5f);
  if(gInput->IsKeyPressed('S')) 
    mCamera->AddAngleX(-0.5f);

  if(gInput->IsKeyPressed('R')) {
    mCamera->Reset();
  }
  gInput->ResetMouseChange();
  mCamera->RotateCamera();
  

  if(gInput->IsKeyPressed(VK_UP)) 
    mCamera->MoveForward();

  if(gInput->IsKeyPressed(VK_DOWN)) 
    mCamera->MoveBackward(); 

Am I heading in the wrong direction and getting this quaternion thing all wrong or am I just missing something tiny?

Share this post


Link to post
Share on other sites
Advertisement
Didn't look at your code, but these look like normal rounding errors to me. Unfortunately, floating point operations do not have an infinite precision. Is it important for your application to have a higher precision than this?

Tom

Share this post


Link to post
Share on other sites
Not really, but the errors accumulate and after a while the view vector is completely off and I'm no longer heading in the direction I am supposed to be facing.

Share this post


Link to post
Share on other sites
Quote:
Original post by gcmonk
Not really, but the errors accumulate and after a while the view vector is completely off and I'm no longer heading in the direction I am supposed to be facing.


If your camera is controlled by the user, I think it's not really a problem. The user will get visual feedback on the rotation and will never be able to see the difference caused by the rounding errors.
If your camera is following some predefined path, then there may be a problem. However, this only the case if your rotations and positions are defined using relative values. Using absolute values makes it easy to remove any errors introduced by the interpolation...

Tom

Edit: spelling :)

[Edited by - dimebolt on July 25, 2005 10:45:29 AM]

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!