Thinking in OpenGL

Started by
15 comments, last by Zakwayda 14 years ago
Hi folks, I'm a relatively experienced programmer and I'm trying to get my head around some opengl concepts. I have a moderate math background and I think I understand the basics of the principles at work. I'd really appreciate a short conversation with some of the opengl gurus here to clarify a few points. Briefly, I'm trying to understand how to implement relative movement. I've seen a number of tutorials that use the familiar sin and cos operations to calculate coordinates after a movement along a specified angle. However, it was my understanding that due to the way the coordinate system works, it is not necessary to do this manually in opengl. If the object is rotated around its own axes, then merely translating along the desired axis is all that is necessary. Is this correct or am I barking up the wrong tree here? I have been able to load a model, rotate on own axes and display. The problem comes when I try to move the object "forward" in the scene, followed by further rotations. 1. Rotate object to "point" a specified direction in the scene. [works] 2. Move the object "forward" [works] 3. Rotate again - this time the rotation occurs around the center of the screen and not the model space. I am thinking that somehow I have to translate to the center of the model before rotating, and the first rotations work by coincidence because before any movement: center of model == center of screen. However, regardless of the order I issue the transformations, I just can't seem to get it right. When the rotation is always around the model axes then the translation is not in the direction the model is facing and vice versa. Here's my draw function:

    //  this sets the model position for the new frame
    model->Update();

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glPushMatrix();
    glLoadIdentity();   
    
    //  rotate the model to orientate correctly
    glRotatef(-90, 1, 0, 0);
    glRotatef(180, 0, 0, 1);

    //  rotations derived from user input
    glRotatef(model->rotation.x, 1, 0, 0);
    glRotatef(model->rotation.y, 0, 1, 0);
    glRotatef(model->rotation.z, 0, 0, 1);

    //  move the object to its current position
    glTranslatef(model->position.x, model->position.y, model->position.z);

    // draw the vertices
    model->Draw();

    glPopMatrix();
    glutSwapBuffers();


Any pointers would be greatly appreciated! FYI the model is a spacecraft and I am trying to get it to fly in the direction it is pointing.
Advertisement
Try this:

    //  this sets the model position for the new frame    model->Update();    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    glPushMatrix();    glLoadIdentity();           //  move the object to its current position *been moved    glTranslatef(model->position.x, model->position.y, model->position.z);    //  rotate the model to orientate correctly    glRotatef(-90, 1, 0, 0);    glRotatef(180, 0, 0, 1);    //  rotations derived from user input    glRotatef(model->rotation.x, 1, 0, 0);    glRotatef(model->rotation.y, 0, 1, 0);    glRotatef(model->rotation.z, 0, 0, 1);    // draw the vertices    model->Draw();    glPopMatrix();    glutSwapBuffers();


The order of matrix multiplications matter. You want to rotate on the origin first, but by translating you moved it off the origin, and then rotated it.

The matrix multiplications are associative. e.g. (a*b)*c = a(b*c) or a*(b*(c*d)) = ((a*b)*c)*d

You should look it up if you don't know what it is, as it will help you understand the multiplications.

Edit:

To fly in the direction, it would be easier to have a vector pointing the direction, and just add that to the model->position.

Also think of the lowest matrix calls acting first, e.g. rotating or translating etc.

[Edited by - andrew111 on April 30, 2010 11:10:32 AM]
Hey thanks for such a quick reply. That is exactly what I had tried. With the translation called first, yes the model always rotates correctly on its axes - but then it always moves "down" the screen regardless of its rotation! (I guess because the translation is now relative to the world coordinate system and not the model)

I feel a "d'oh!" moment coming on. I'm sure I must be doing something stupid!
Here are a few bits of information that might be helpful...

First of all, the problems you're talking about aren't really OpenGL-specific. It's understandable why one might think they are, but this is really just math that we're talking about, which is essentially the same regardless of what API you're using (OpenGL, Direct3D, etc.).

There are some (sort of) API-specific things that can hang people up though, and one of these is the way in which OpenGL's 'transform' functions operate. Before I continue though, let me point out that the functions you're using are actually deprecated and are more or less irrelevant in modern OpenGL code (typically, you would instead build these transforms yourself and then pass them in as shader parameters).

In any case, one of the most common causes of confusion when working with OpenGL is transform order. As I'm sure you know, matrix multiplication is not commutative, and the order in which a sequence of transforms is applied matters. For example, a rotation followed by a translation will generally have a different result than a translation followed by a rotation. Anytime someone says that they're expecting a model to rotate about its own origin but it instead appears to be rotating about some other point, you can bet that they're applying the transforms in the wrong order, and looking at the bit of code you posted, that appears to be the case here.

I think if you move the glTranslatef() call to before the calls to glRotatef(), you'll find that the behavior is correct (or at least closer to what you're expecting).

I'm sure someone's already pointed that out by now (in fact, after refreshing, I see that they have :), but I'll go ahead and elaborate a bit on this.

You may already know this, but the order in which matrices must be multiplied to yield a given effect depends on whether row or column vectors are being used. In short, when row vectors are used, the matrix product A*B applies the transforms in the order A->B, and when column vectors are used, the transforms are applied in the order B->A.

OpenGL (or the OpenGL fixed-function pipeline at least) has traditionally been thought of as using column vectors, although I don't know whether this really has any practical significance. What is significant though is that the most recent transform command issued is 'closest to' the vector to which it is to be applied, so to speak. This means that transforms are actually applied in the reverse of the order in which they appear in your code, and is why the translation actually needs to come first in your example in order to have the effect of a rotation followed by a translation.

As mentioned previously though, this really isn't the way things are done anymore. In modern OpenGL, you would typically build these transforms yourself, and then simply upload them to OpenGL as shader parameters. On your side of things, you can use whatever convention you want (e.g. row or column vectors), so long as the matrix data ends up in a form that OpenGL can understand.
As for your follow-up post, can you post your revised rendering code, along with the code you're using to update the object's position?
Hi thanks for the info. I'm certainly no math expert but I do understand the concepts you are raising. And I understand that the gl functions are merely helpers to construct a matrix, which is then applied to the vertices. I had read that they are now depreciated and the modern approach is to construct your own transformation matrices, however I thought it could still be a good approach to learn the basics.

I altered my drawScene function precisely as suggested by the poster above, and my code to update the model position merely increases its "position.y". The reason for that is because in the local model coordinate system, it "points" along its y axis and therefore I concluded that "forward" for the model is along the y axis. This is probably my problem, right? Although I can't quite get my head around why.

I'm assuming then that I have to do something a little more sophisticated to the model position vector?

Many thanks for the help, greatly appreciated!
Quote:Original post by hig
Hi thanks for the info. I'm certainly no math expert but I do understand the concepts you are raising. And I understand that the gl functions are merely helpers to construct a matrix, which is then applied to the vertices. I had read that they are now depreciated and the modern approach is to construct your own transformation matrices, however I thought it could still be a good approach to learn the basics.
Oops - well I guess that post was mostly redundant then :)
Quote:I altered my drawScene function precisely as suggested by the poster above
I suggested that as well.
Quote:and my code to update the model position merely increases its "position.y". The reason for that is because in the local model coordinate system, it "points" along its y axis and therefore I concluded that "forward" for the model is along the y axis. This is probably my problem, right? Although I can't quite get my head around why.

I'm assuming then that I have to do something a little more sophisticated to the model position vector?
Right, that won't do what you're expecting. Why it won't do what you're expecting is actually a little tricky to explain, but here goes.

To keep things simple, let's assume that your model transform only incorporates rotation and translation (which is fairly common, and appears to be the case here, judging from your code).

These two transforms (rotation and translation) are typically applied in the order rotation->translation. If you hold some object in your hand (e.g. a toy car, spaceship, or whatever), you can visualize this by starting at a 'default' position and orientation ('identity' in matrix terms), then rotating it to the orientation that you want, and then translating it to the position that you want.

Note that because the rotation occurs first, the translation is unaffected by the rotation. If the translation is +5 along the y axis, then the model will be first rotated to the desired orientation, and then moved 5 units along the (world) y axis.

In order to get the model positioned where you want, your position variable (model->position in your code) needs to represent the object's actual position in world space. If all you ever do is move this position vector along the y axis, then your model will always be positioned somewhere along the (world) y axis (it won't, in general, move 'forward' based on the object's orientation).

To get the kind of motion you want, you need to translate the position vector not along the y axis (or whatever), but rather along the object's current direction vector(s).

There are a few ways to get these direction vectors, but the easiest way is probably to build a transform matrix for the object and then extract the direction vectors from the first three rows or columns of the matrix. (This can be done using OpenGL functions, but it's more straightforward if you use your own math code or a third-party math library for this.)
That helps a lot, thanks. My main misconception was that opengl inherently handles this "relative" movement internally, knowing the "local" orientation of the vertices being drawn.

Is it then analogous to the operations required in 2d space to translate at an angle? i.e sine and cosine calculations? I assume it must be, only performed directly with matrices?

Although you're right, and the discussion is moving further away from opengl and more towards independent math topics!

But I now know the specific area that I need to investigate more is the general math behind it and not specifically the opengl API.

Thanks again.
Quote:My main misconception was that opengl inherently handles this "relative" movement internally, knowing the "local" orientation of the vertices being drawn.
Yup, no such luck :) OpenGL neither knows nor cares about that stuff - it has no concept of 'object motion', 'local orientation', or anything of that sort. In the fixed-function pipeline, it just applies a series of transforms in matrix form to the input data (the programmable pipeline doesn't even do that much - with the PP, transforming of the input data is entirely up to you).
Quote:Is it then analogous to the operations required in 2d space to translate at an angle? i.e sine and cosine calculations?
Yes, it can be. In 2-d, you typically see something like this (or a vector version thereof):
x += cos(angle) * speed * dt;y += sin(angle) * speed * dt;
If you're dealing with Euler or spherical angles, you can do something similar in 3-d as well.
Quote:I assume it must be, only performed directly with matrices?
You can use either matrices or angles in both 2-d and 3-d. In 3-d especially, using matrices is more straightforward, IMO, and is the approach I'd recommend. (What I'm referring to here is building a transform matrix for the object, and then extracting the direction vectors from the matrix. Note that once you have the transform matrix, it can be uploaded directly to OpenGL via glLoad/MultMatrix*(), allowing you to bypass convenience functions such as glRotate*() entirely.)
Excellent summary, well explained. Cheers!

This topic is closed to new replies.

Advertisement