PDA

View Full Version : Understanding World, Eye (Camera), Model



deegee
10-05-2010, 09:24 PM
Hi

I have been programming for a little while in OpenGL but I seem to have a misconception about how the pipeline manages the order of rendering from Model to View to World.

In everything I have read, including the Red Book and SuperBible, it states that (for example) transforms preceeding a mesh object occur in that object's Model space (local object space).

ie.

glPushMatrix();
glRotatef(45, 1, 0, 0);
<draw_object>
glPopMatrix();

will apply a rotation of 45 degrees on the object's local space along the X vector.

So, if we had the pseudo-code of:


glViewport(...);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
SetPerspective(...);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

glClear(...);
SetLights(...);
glPushMatrix();
LookAt(...);

glPushMatrix();
glRotatef(45, 1, 0, 0);
<draw_object>
glPopMatrix();

...

glPopMatrix();

We would end up with "object" rotated 45 degrees in the view.


My (apparently incorrect) understanding was that within this set of code, we would have three distinct spaces.

Defines World space:


glViewport(...);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
SetPerspective(...);

Defines our Camera/Eye (View) space:


glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

glClear(...);
SetLights(...);
glPushMatrix();
LookAt(...);

Occurs at local Model (object) space:


glPushMatrix();
glRotatef(45, 1, 0, 0);
<draw_object>
glPopMatrix();


I always assumed that the glRotatef would modify only the object vertex data so the result would be a correctly lit object.
However, I must be incorrect and apparently View and Model are within the same space, as the rotated object will also have rotated lighting on it, in other words, it's lighting will be incorrect (rotated 45 degrees away from the actual light position set in SetLights(...);).

SetLights(...); occurs in the Identity Matrix of ModelView so it is a fixed (stationary) light source (see RedBook 6th Edition pg.202).


So am I to understand that there is no real "local object space" for direct object transformations, just World and ModelView space, and any transforms such as rotation that are to be applied to a model prior to lighting must be done independantly and prior to sending the object to OpenGL, ie. through directly manipulating the object's vertex array prior to DrawElements or whatever is being used for the object.

I have an understanding of matrices etc., and I believe I see why this would be occurring, however I was under the assumption that OpenGL rendered in an in-to-out and bottom-to-top order. So I assumed that the object's glRotate would occur on its vertex data array prior to the View space matrix set in LookAt.
But apparently the matrix in glRotate is multiplied with LookAt before being applied to the object vertex data so the result is the entire ModelView space is modified for this object, hence the light direction within this rotated space also changes, and the lighting then also rotates 45 degrees (??).

Am I correct in my last assumption, or did I just confuse you all. :-)
I hope I was clear in all of this.

Thanks.

Alfonse Reinheart
10-05-2010, 10:15 PM
But apparently the matrix in glRotate is multiplied with LookAt before being applied to the object vertex data so the result is the entire ModelView space is modified for this object, hence the light direction within this rotated space also changes, and the lighting then also rotates 45 degrees (??).

Your problem is here:



glClear(...);
SetLights(...);
glPushMatrix();
LookAt(...);


Before the call to "LookAt", the GL_MODELVIEW matrix is identity. All of the lighting calls will transform the light vector you give it by the current GL_MODELVIEW matrix.

Remember: your "LookAt" function transforms things from world space to the space expected by the projection matrix (called "eye space" or "camera space"). Everything "above" the "LookAt" matrix is in world space. Since your lights are expected to be provided in world space, OpenGL will multiply them by GL_MODELVIEW in order to move them to transform them from world space to camera space.

This means that the light position/direction will be in the same space as vertices and normals that have been transformed by GL_MODELVEIW.

So do your lighting after calling "LookAt".

deegee
10-05-2010, 11:09 PM
Hi,


So do your lighting after calling "LookAt".

Thanks. I follow what you are saying but the results I get are the same. I'm using only Ambient and Light0 which is a Directional.

If I do:



// Set Projection

glViewport(...);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
SetPerspective(...);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

...

// Render method

glClear(...);
glPushMatrix();
LookAt(...);
glLightfv(GL_LIGHT0, GL_POSITION, m_flaLight0Position); // set directional light vector

glPushMatrix();
glRotatef(45, 1, 0, 0);
<draw_object>
glPopMatrix();

...

glPopMatrix();

The lighting on the object is still also rotated off by 45 degrees. This really has me stumped.

deegee
10-05-2010, 11:28 PM
hmmm...

There seems to be some odd glitch somewhere in my code.
Debugging time. Thanks for your help.

deegee
10-05-2010, 11:46 PM
To fix it I had to put the following two lines to reset the model matrix at the top of my render method:

glMatrixMode(GL_MODELVIEW); <-- this
glLoadIdentity(); <-- this
glPushMatrix();
LookAt(...);
glLightfv(...);
...
glPopMatrix();

Which is odd because they are already set in the SetProjection method and are then never changed in the render method. SetProjection is called during window resizes etc.

I use glFlush() and SwapBuffers, at the end of the render code, why would that change the matrix mode or throw off the stack from being Identity... (??).

BionicBytes
10-06-2010, 04:15 AM
it does doesn't.
It's more likely you have missed a glpopmatrix.
My advise: instead of relying on push/pop try saving the modelview to a matrix of your own and then use glLoadMatrix instead; you'll find this quicker and more flexible.

deegee
10-06-2010, 08:36 AM
I thought of a missing pop also, but I triple-checked my code and there are no missing pops.
I use very little immediate code so there are not many pushes and pops.
I shortened my render loop to what I had listed above, where there was only two pairs of push/pop and it still had the issue.

Plus if there were a missing pop I should run out of stack within a few rendered frames and the rendered scene should then fail.

Thanks for the advice though about using LoadMatrix.

EDIT:

I found the bug. :-)
It wasn't a missing pop. I have a function for printing render stats on screen using a bitmap font, and it doesn't push/pop the current matrix. So it is resetting the matrix on me every frame.

BionicBytes
10-06-2010, 09:55 AM
I had a similar issue with my engine. I had always had the principle to returning the GL state to what it was prior to calling any function, so a sort of push/pop mentality.
After my engine got really complicated...this was so hard to maintain and I switched to explicitly setting the state instead. Now I don't waste my time saving various bits of state - I just set them as needed. Same with the camera class - it loads the matrix it wants. Much simpler....

deegee
10-10-2010, 12:43 PM
Yes, that is what I ended up doing when I fixed the bug.

I must have had a bad day when I wrote part of the code for the stats output as it was wonky. :o
I am an experienced programmer but fairly new to OpenGL so I've refactored my engine a few times as I've learned more over the past few months.

ugluk
10-10-2010, 02:35 PM
For basic stats, you can use the title bar of your window.

deegee
10-12-2010, 10:17 AM
Thanks.
It is a few lines of scene stats that can be toggled on and off, so on the viewport corner works best.