问题
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;
}
回答1:
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.
回答2:
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.
回答3:
Try manually setting normal values for each vertex.
回答4:
I ran into the same problem and got the solution. The problem was that I set the light position only in the OnLoad part of the program... I think it is the best to post some code. I work in C# using OpenTK cause I'm aware I will never enter graphic business. :)
The result is a teapot or some other object that is rotating with the light that has fixed position. I can move the eye position (LookAt method) and the light source stays still. It's just like in the real word: The object can spin but the sun will not. :) Also, we can walk around the object but the sun still remains unimpressed and stays still. This code will do that trick.
protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
System.Threading.Thread.Sleep(10); //useful thing to lower CPU usage
//I can set the eye position at runtime, it's not necessary to have this
modelview = Matrix4.LookAt(this.eye, Vector3.Zero, Vector3.UnitY);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadMatrix(ref modelview);
//**this is the key - you have to set the light position every time**
GL.Light(LightName.Light0, LightParameter.Position, new float[4] { 1000, 1000, 0, 0 });
// We don't want the walls to rotate so we push the current matrix
GL.PushMatrix();
GL.Rotate(move, Vector3.UnitY);
move += 0.5f;
GL.Color3(1.0f, 1.0f, 1.0f);
GL.Begin(BeginMode.Triangles);
//the object that will rotate
GL.End();
GL.PopMatrix();
GL.Begin(BeginMode.Quads);
//some quads (walls) in the background that will stay still
GL.End();
SwapBuffers();
}
I took this from my program and deleted a lot of stuff that is here unimportant so maybe it looks a bit weird. I hope it helps.
回答5:
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.
回答6:
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();
来源:https://stackoverflow.com/questions/1757958/how-do-i-fixate-a-light-source-in-opengl-while-rotating-an-object