views:

619

answers:

4

I have a glutSolidTeapot (which has its surface normals generated automatically according to opengl.org) and a light source which is emitting diffuse light. The problem comes when I try to rotate the teapot: it seems like the light source is doing the rotation as well, not remaining in the same position I defined it (it essentially follows the teaspot). As you can see in my code, I only modify the lighting position upon initialization, so it is not subjected to glRotatef(), since its called after setting the light position.

Despite spending numerous hours trying to solve this problem, I really have no idea what this kind of behavior can be attributed to.

Pasting glEnable(GL_NORMALIZE); in the initialization does not solve the problem neither.

I think the desired output should be a teapot with a shiny right side (since the light is coming from that direction), no matter what angle the teapot is rotated by.

If you want to test my code, press Space to rotate the teapot.

#include <math.h>
#include <stdlib.h>

#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
#include <windows.h>     
#endif

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>     

void onInitialization( ) { //creating the light source
 glEnable(GL_LIGHTING);
 glEnable(GL_DEPTH_TEST); 

 GLfloat diffuse[]={0.8, 0.8, 0.8, 1.0};
 GLfloat pos[]={0.5, 0.0, 0.8, 0.0};

 glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
 glLightfv(GL_LIGHT0, GL_POSITION, pos);

 glEnable(GL_LIGHT0);

 glRotatef(-90, 1, 0, 0); //we want to see the top of the teapot
}

void onDisplay( ) {
    glClearColor(0.1f, 0.2f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 //rotating on every frame (for testing purposes only)
 glRotatef(5, 0,1,0);
 glutSolidTeapot(0.4);

    glFinish();
    glutSwapBuffers();
}


void onKeyboard(unsigned char key, int x, int y) {
 if (key==32){ //do rotation upon hitting the Space key
  glutPostRedisplay();
 }
}

int main(int argc, char **argv) {
    glutInit(&argc, argv); 
    glutInitWindowSize(600, 600);
    glutInitWindowPosition(100, 100);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
    glutCreateWindow("Teapot");

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    onInitialization();

    glutDisplayFunc(onDisplay);
    glutKeyboardFunc(onKeyboard);

    glutMainLoop();

    return 0;
}
A: 

The FAQ states:

A light's position is transformed by the current ModelView matrix at the time the position is specified with a call to glLight*().

In other words, lights aren't positioned in some kind of magic static "world coordinate system"; they're multiplied by the modelview matrix just like any geometry would be. This makes sense; you often want to have lights locked to geometry.

I also think your application "destroys" the modelview matrix; it's never re-computed from scratch. You should start each frame with a LoadIdentity(), then emit the lightsource, then rotate as desired, and finally emit the geometry.

unwind
If the modelview matrix were destroyed, the rotation I use wouldn't work, I think. Anyway I tried what you suggested (again, because I previously tried something like this), but it didn't work. I know that the position of lights are multiplied by the current matrix, but they should only be multiplied upon intitialization, and never, ever after that, since I don't touch it.
kahoon
+1  A: 

I think changing

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

to

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

is a good place to start.

MaR
Thank you, you helped a lot.
kahoon
The point being that the last MatrixMode stays active.
Andreas
+1  A: 

I also think your application "destroys" the modelview matrix; it's never re-computed from scratch. You should start each frame with a LoadIdentity(), then emit the lightsource, then rotate as desired, and finally emit the geometry.

This doesn't work, this doesn't work, this doesn't work, lalalalalalalala, and the FAQ is patently wrong, plain and simple.

Emitting the light just after glLoadIdentity() in order to get a camera-bound light will work in simple cases when you only translate, or only rotate the model around the origin. But when you start doing non-trivial, non-hello-world things like rotating an object around a point different than the origin (translate(-v), rotate(angle), translate(v)) and then moving it so that the rotation center is brought in front of the camera, then the "load identity, place light" technique starts failing spectacularly and in ways that make it very hard to deduce what is even going on.

No, I don't know the solution.

BASTA
A: 

In my opinion what you should do is not only to call glLoadIdentity() at the beginning of your render routine, but to push and pop the modelview matrix.

for a scene with a globally fixed light source and two objects which are indepently positioned and rotated this pseudo code will do the trick:

glLoadIdentity();

// first object
glPushMatrix();
glTranslate(...);
glRotate(...)
glPopMatrix();

// second object
glPushMatrix();
glTranslate(...);
glRotate(...);
glPopMatrix();
Holger Kretzschmar
I'm doing that, but the light still moving. Also I'm doing what is said in other answers :(
David Ameller