Camera problems

Moderator
Posts: 1,562
Joined: 2003.10
Post: #1
I'm trying to write a camera class. So far, it only does basic stuff, but even that doesn't appear to be working properly.

Code:
typedef struct Camera Camera;

struct Camera {
  Quaternion orientation;
  Vector position;
};

void CameraApply(Camera * camera) {
  Vector forward, up, focusPoint;
  
  glLoadIdentity();
  forward = VectorWithValues(0.0, 0.0, -1.0);
  forward = QuaternionRotateVector(&camera->orientation, &forward);
  up = VectorWithValues(0.0, 1.0, 0.0);
  up = QuaternionRotateVector(&camera->orientation, &up);
  focusPoint.x = (camera->position.x + forward.x);
  focusPoint.y = (camera->position.y + forward.y);
  focusPoint.z = (camera->position.z + forward.z);
  gluLookAt(camera->position.x, camera->position.y, camera->position.z,
            focusPoint.x, focusPoint.y, focusPoint.z,
            up.x, up.y, up.z);
}

void CameraOrbitPoint(Camera * camera, Vector point, Vector axis, float angle) {
  float distance;
  Quaternion quat;
  
  distance = sqrt(((camera->position.x - point.x) * (camera->position.x - point.x)) +
                  ((camera->position.y - point.y) * (camera->position.y - point.y)) +
                  ((camera->position.z - point.z) * (camera->position.z - point.z)));
  
  camera->position.x -= point.x;
  camera->position.y -= point.y;
  camera->position.z -= point.z;
  
  VectorNormalize(&camera->position);
  quat = QuaternionFromVector(&camera->position);
  QuaternionRotate(&quat, axis, angle);
  camera->position = QuaternionToVector(&quat);
  
  if (distance != 0.0) {
    camera->position.x *= distance;
    camera->position.y *= distance;
    camera->position.z *= distance;
  }
  
  camera->position.x += point.x;
  camera->position.y += point.y;
  camera->position.z += point.z;
}

void CameraLookAtPoint(Camera * camera, Vector point) {
  point.x -= camera->position.x;
  point.y -= camera->position.y;
  point.z -= camera->position.z;
  VectorNormalize(&point);
  camera->orientation = QuaternionFromVector(&point);
}

I'm trying to get it to orbit around an object at {0.0, 0.0, -3.0} in global coordinates, like so:

Code:
CameraOrbitPoint(camera, VectorWithValues(0.0, 0.0, -3.0), VectorWithValues(0.0, 1.0, 0.0), (M_PI / 100.0));
CameraLookAtPoint(camera, VectorWithValues(0.0, 0.0, -3.0));

What this is supposed to look like is the object the camera is pointing at rotating in place. Instead, it looks more like the camera is rotating in place, but that's not quite what it's doing either. It's rather difficult to describe...

Anyway, can anyone see anything wrong with the above code? I've been staring at it for hours, and it seems airtight. Any help would be greatly appreciated...

Alex Diener
Quote this message in a reply
Moderator
Posts: 1,562
Joined: 2003.10
Post: #2
Thanks to OSC, I've got the code in much better working order. It's still not quite behaving right, though...

I'm storing the camera's orientation as a quaternion, and using that to transform a look vector and an up vector for gluLookAt, like so:

Code:
void CameraApply(Camera * camera) {
  Vector forward, up, focusPoint;
  
  glLoadIdentity();
  forward = VectorWithValues(0.0, 0.0, -1.0);
  forward = QuaternionRotateVector(&camera->orientation, &forward);
  up = VectorWithValues(0.0, 1.0, 0.0);
  up = QuaternionRotateVector(&camera->orientation, &up);
  focusPoint = VectorAdd(camera->position, forward);
  gluLookAt(camera->position.x, camera->position.y, camera->position.z,
            focusPoint.x, focusPoint.y, focusPoint.z,
            up.x, up.y, up.z);
}

This appears to work. The problem is with the function that's supposed to take a point, and make the camera face toward it:

Code:
void CameraLookAtPoint(Camera * camera, Vector point) {
  Vector direction, axis, forward;
  float angle, dotProduct;
  
  forward = VectorWithValues(0.0, 0.0, -1.0);
  direction = VectorSubtract(point, camera->position);
  VectorNormalize(&direction);
  dotProduct = VectorDot(direction, forward);
  /* Need to somehow account for the case where direction and forward are the same, in which case cross product wouldn't work */
  axis = VectorCross(direction, forward);
  angle = acos(dotProduct);
  camera->orientation = QuaternionFromAxisAngle(axis, angle);
}

This only sometimes works. If the camera rotates more than 90∞ in either direction, or if it's above or below the point it's supposed to be looking at, it starts doing all sorts of crazy things. Here's the binary so you can see for yourselves:

http://www.sacredsoftware.net/temp/Skele...st.app.sit

Arrow keys rotate the camera, A and Z zoom in and out. Brackets do stuff too, but don't worry about that at the moment. Anyway, I'd really really appreciate some help with this, as uDevGames starts in 2 days, and I'd like to base the camera code for my entry off this code. If some of the code doesn't make sense, I can explain it in greater detail.

Someone? Please?

Alex Diener
Quote this message in a reply
Moderator
Posts: 1,562
Joined: 2003.10
Post: #3
In case anyone is interested, I finally came up with a solution today. Since quaternions were turning out to be so impossibly difficult to manage, I gave up on them and used a direction vector instead:

Code:
typedef struct Camera Camera;
struct Camera {
  Vector orientation;
  Vector position;
};

void CameraApply(Camera * camera) {
  Vector forward, up, right, focusPoint;
  
  glLoadIdentity();
  forward = camera->orientation;
  right = VectorCross(VectorWithValues(0.0, 1.0, 0.0), forward);
  VectorNormalize(&right);
  up = VectorCross(forward, right);
  VectorNormalize(&up);
  if (up.y < 0.0) {
    /* Upside down up vector; flip it */
    up.y = -up.y;
  }
  focusPoint = VectorAdd(camera->position, forward);
  gluLookAt(camera->position.x, camera->position.y, camera->position.z,
            focusPoint.x, focusPoint.y, focusPoint.z,
            up.x, up.y, up.z);
}

void CameraLookAtPoint(Camera * camera, Vector point) {
  camera->orientation = VectorSubtract(point, camera->position);
  VectorNormalize(&camera->orientation);
}

Works absolutely wonderfully. The only potential gotcha is if the camera is asked to look straight up or straight down, but that's not an issue for me because my app constrains the camera so it can't do that.

Oh, and thanks for all the help, guys. Rasp

Alex Diener
Quote this message in a reply
Krevnik
Unregistered
 
Post: #4
Ew, ew, ew (no offense)... yeah, there are still some nasties left with that. For example, you are doing quite a bit of what gluLookAt() already does by doing the cross product of your orientation and 'up', and then doing another cross product to get the real 'up' for the camera, so there are some operations which get repeated there. Another thing I see is that the camera cannot roll. While this might not be vital, it does make it difficult. Might I recommend that a UVN vector orientation algorithm be looked into? I implemented UVN vectors in my camera object and I don't even need to use gluLookAt() for the camera anymore, it is just the formation of a matrix and multiplying onto the stack, 2 function calls. I did the same for all my objects as well with good effect, and you can can easily convert the N vector (forward view vector) into a Quaternion for slerped rotations internally as well. Not to mention that a full UVN vector implementation removes that gotcha you have.

If you don't need any of this, feel free to ignore me, it is just that I spent a day going through this myself and understand the annoyances. Cameras are the real devil of early 3D engine development.
Quote this message in a reply
sylia
Unregistered
 
Post: #5
hey krevnik,

i just started opengl and i'm trying to do the same thing, orient and move the camera without using gluLookAt(). I know this is pretty baskc but what are UVN vectors and how to I implement them? and when you multiply the matrix onto the stack, is it something like glMultMatrixf(mv.mat_array)?
Quote this message in a reply
Post Reply