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:
- extract right, up and front vectors
- rotate by some amount around a given axis
- 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. |
| |
|