Q: How do I make a camera move independently from my objects in OpenGL?

I've answered this question frequently enough.

The long answer is that you really need to understand matrix math, and what the OpenGL transform pipeline is. Meshes/geometry are issues in "model space" where typically 0,0,0 is the "feet" of the model. These are then transformed by the model matrix, to put them into 'world' space; then they are transformed by the view matrix, to put them into 'camera' space. Because this transformation is usually always done in one go; GL combines these into the MODELVIEW matrix for efficiency.

Typically, you'll want to construct a matrix for your camera, then a matrix for your model position/orientation, then multiply them together to create your modelview, and load that using glLoadMatrix(). This means you re-create the modelview for each entity you want to render (but the camera matrix can stay intact across a frame).

If this is too much for you to do right now and you just want a system where a camera can move around, pitch around its "side" axis, and rotate around its "up" axis, and entites are places at a position, with a rotation around their "up" axis, then the following quick-and-dirty code suggestion may help.


void renderScene()
{
  glMatrixMode( GL_PROJECTION );
  glLoadIdentity();
  glFrustum( fovy, aspect, znear, zfar );

  // start fresh
  glMatrixMode( GL_MODELVIEW );
  glLoadIdentity();

  // set up camera
  glRotatef( 0, 1, 0, -camHeading );
  glRotatef( 1, 0, 0, -camPitch );
  glTranslatef( -camPos.x, -camPos.y, -camPos.z );

  for(
    MyEntity * ent = entities;
    ent;
    ent = ent->next )
  {
    // save the camera matrix
    glPushMatrix();

    // position and rotate the entity
    glTranslatef( ent->pos.x, ent->pos.y, ent->pos.z );
    glRotatef( 0, 1, 0, ent->heading );

    // support multi-pass drawing
    ent->beginDrawing();
    for( int pass = 0; ent->setupPass( pass ); ++ent ) {
      ent->drawPass( pass );
    }
    ent->endDrawing();

    // restore the camera matrix
    glPopMatrix();
  }
}

This code is not super efficient, but it's also not super inefficient -- chances are, if you have performance problems, they're not in this code but somewhere else.

To move the camera, simply change "camPos" to put it where you want. To move an entity, simply change "ent->pos" to be where you want it. To rotate the camera or the entity, change camHeading, camPitch, or ent->heading appropriately.

This is actually all you need to make a simple world where you can drive the camera around, and have objects independently move around the world; i e a simple shooter like "Quake" could be done with this camera control. However, for more realistic simulations, you'll need full degrees of freedom in both how the camera is oriented, and how entites are oriented. At that point, please rip out this version of renderScene() and put in something real instead.


Compare to Gimbal Lock

Gimbal lock is a chimera. Any orientation can be expressed using Euler angles. Gimbal lock is just a ghost in your brain, when you're trying to accomplish some special thing using the wrong numbers -- rotating an existing Euler orientation around the X angle is NOT the same thing as changing only the X value of that orientation. Rotating around the X angle may mean changing all three values, depending on what the original orientation was.

A perfectly fine camera representation is one where you represent heading, pitch, and roll. You would turn this into a matrix (or quanternion) by first rotating around your right vector by the pitch; then rotating around your PREVIOUS up vector by the heading; then rotating around your NEW front vector by the roll.


  rotation cameraRot = identity;
  vector right = cameraRot.right();
  vector up = cameraRot.up();
  cameraRot.rotateAround( right, pitch );
  cameraRot.rotateAround( up, heading );
  vector front = cameraRot.front();
  cameraRot.rotateAround( front, roll );

Any orientation concept you choose to implement (quaternions, Euler angles, Matrices, etc) will have a few basic operations:

  1. extract right, up and front vectors
  2. rotate by some amount around a given axis
  3. extract to matrix for feeding to a graphics API
Using only these operations, the camera above represents everything you'll need, and is independent of whether the representation is Euler, Quaternion, or something else.
See also this simple scene graph approach.