using gluLookAt properly

Despite much research on the topic, I have never been able to get gluLookat to work properly.

The problem is that with a fully dynamic camera, allowing the position to change produces what I can only describe as a drunken loss of control - everything starts off fine, but after a few seconds rotational input seems to have a spiral factor added to it, insofar as a pure roll input will seem to have a pitch component also. The position also seems to be updated incorrectly, but i’m at a loss to describe how, except that there’s a sort of “drift”…

I don’t have faulty code handy at the moment, but details of implementation as follows:

I have a left handed (x increases to the right) camera structure, with 3 axis vectors and a position (camera origin) vector.

Rotations about it’s own axes on this structure work properly, i’ve used the same code in simple pure software tests.

Basic outline of setup code is below:

Update camera structure for next frame.
Select projection matrix
Load identity
Use gluPerspective(angle,-(aspect),1.0,100.0)
Select modelview matrix
Load identity
Use gluLookAt(
cam.O.x,cam.O.y,cam.O.z,
cam.Z.x,cam.Z.y,cam.Z.z,
cam.Y.x,cam.Y.y,cam.Y.z
)

Note the negation of aspect, which gives me x increasing to the right on the screen…

cam.O is the camera’s origin, cam.Y is the camera’s Y axis, cam.Z is the camera’s Z axis, all in world coordinates.

That gives me the effect described above.

Initially, I was putting the gluLookAt on the projection matrix; I fixed that, but there was no difference.

I’ve tried doing away with gluPerspective and replaced it with glFrustum (can’t remember if I did that so that x remained increasing to the right), but there was no difference.

Doing this makes it work perfectly, regardless of things like using glFrustum instead of gluPerspective:

Update camera structure for next frame.
Select projection matrix
Load identity
Use gluPerspective(angle,-(aspect),1.0,100.0)
Select modelview matrix
Load identity
glTranslatef(-cam.O.x,-cam.O.y,-cam.O.z)
Use gluLookAt(
0.0,0.0,0.0,
cam.Z.x,cam.Z.y,cam.Z.z,
cam.Y.x,cam.Y.y,cam.Y.z
)

Not sure if i’ve been putting the translate before or after the lookat - does it matter?

Anyone know what i’m doing wrong?

Translatef should come after lookat, since you first want to make world space vertices eye relative, then you want to rotate them into eye space (what your lookat call amounts to). That’s my guess.

The view matrix is the inverse of the camera matrix. Take whatever transforms you would use to place a model of a camera in the world, then invert them for the modelview.

Why not just use right handed system like the rest of us? :wink:

Oh and make sure you up vector != forward vector! That’s bad magic.

(up.vector!=forward.vector)==true;

Yeah, I think I’ll experiment with just putting the view matrix in directly from the coefficients of my camera class…

Am I correct in thinking that if I calculate the values for the components of the world’s origin and axes, expressed in the camera’s coordinate system, and put these values in the right positions in an array of 16 floats, then I’ll have a modelview matrix that should definitely work?

I only have 12 values in my camera class.

If I’m correct above, the world to camera space transformation (or inverse camera matrix) produces

X.x Y.x Z.x
X.y Y.y Z.y
X.z Y.z Z.z
O.x O.y O.z

(I think) as a 3 column by 4 row matrix in normal order, all those values being members of the camera structure. But I need a 4x4 matrix for OpenGL, so I need to add an extra column on the right hand side, butwhat should do I put in it, before reordering it? Given that I have three vectors and a point, should the whole matrix be this,

X.x Y.x Z.x 0.0
X.y Y.y Z.y 0.0
X.z Y.z Z.z 0.0
O.x O.y O.z 1.0

before reordering?

perhaps youre seeing gimbal lock, ii suppose it depends on how many degrees of freedom u wish if u want 6 (like a flight simmulation)
i wouldnt use glulookat but
something like (bogus code just for idea )

static mat44 current_mat;
mat44 Yaxis_rot;
mat44 Xaxis_rot
Yaxis_rot.create_rotmatY( 0.01 * mouseY );
current_mat = current_mat * Yaxis_rot;
Xaxis_rot.create_rotmatX( 0.01 * mouseX );
current_mat = current_mat * Xaxis_rot;

this way u can accumulate as many rots as u want without it going haywire (u may need to renormalize the matrix every so often though)

glLoadMatrix( current_mat );

Hmmm, gimbal lock shouldn’t be a problem - the camera coordinate system is defined in world coordinates, and rotations on the camera swivel it around one of its own axes, producing a result which is also in world coordinates, in a second variable of the same type…

The general usage involves either a roll and a pitch, or a yaw and a pitch (this is certainly the case in the odd situation above, i’ve tried both), and goes like this:

rotAxesZ(&axes,sinRoll,cosRoll,&result); //roll
rotAxesX(&result,sinPitch,cosPitch,&axes); //pitch

…and then the values from axes get plugged into gluLookAt.

The angles involved are generally very small, much less than 90 degrees.

I’m beginning to get a nasty feeling that the cause of this is me trying to mix left and right handed systems to avoid rewriting some very dull legacy code, although it’s odd that just adding a glTranslatef fixes that…

Will rewrite my vector coordinate system code for a right handed system, and try both gluLookat and direct matrix creation…

Thanks, folks!

Below is the code I came up with, which successfully sets up a viewing transformation with a single glMultMatrix. It doesn’t seem to matter whether I use zero or one in array positions 3,7, and 11, though (those being the indices in the matrix which control scaling, I believe).

Should I leave those values at zero, or might doing so mess up some other operation further down the line?

typedef struct
{
float x,y,z;
}
cj3DMTO_vector3f;

typedef struct
{
cj3DMTO_vector3f X,Y,Z,O;
}
cj3DMTO_cSystem4v;

typedef float cj3DMTO_OGLMatrix[16];

void cj3DMTO_CSystemToCameraMatrix(cj3DMTO_cSystem4v *subject, cj3DMTO_OGLMatrix result)
{
result[0]=(subject->X).x;
result[1]=(subject->Y).x;
result[2]=(subject->Z).x;
result[3]=0.0;

result[4]=(subject->X).y;
result[5]=(subject->Y).y;
result[6]=(subject->Z).y;
result[7]=0.0;

result[8]=(subject->X).z;
result[9]=(subject->Y).z;
result[10]=(subject->Z).z;
result[11]=0.0;

result[12]=-dotProduct(subject->X,subject->O);
result[13]=-dotProduct(subject->Y,subject->O);
result[14]=-dotProduct(subject->Z,subject->O);
result[15]=1.0;

}

Those positions are for translation not scaling.

it’s perhaps better not to think of positions in a matrix as serving a particular purpose, since matrices can be scaled, transposed and inverted, so the meaning of a particular row or column, much less a particular position, can change.

opengl matrices are just a transpose of c/c++ matrices. that’s the only difference. you can manipulate your matrices using standard math then just transpose them when supplied to opengl.

there’s an extension that allows you to pass your row-major matrices as is, if this transpose business troubles you.

you might even consider working with column major matrices to begin with. if you’ve ever dabbled with sse/3dnow! you’ll understand the virtue in doing this.

Actually, that was a dumb question I asked, because now that I look at the redbook again, those indices, while not for translation, also aren’t for scaling, either.

All the example matrices in the redbook keep those entries as zero, so that seems like the answer…

Thanks again, all…

I, too, go for the column-major matrices. This can make your life somewhat easier, in the end, especially if you’re optimizing for SSE/3DNOW!.

The OpenGL “Redbook” appendices print matrices in row-major format, so as to be a bit more familiar, I suppose; but this might turn out to be more confusing, to some.