OpenGL:移动相机时如何使光线静止?

Posted

技术标签:

【中文标题】OpenGL:移动相机时如何使光线静止?【英文标题】:OpenGL: How to make light stationary when moving the camera around? 【发布时间】:2016-12-18 19:09:26 【问题描述】:

我正在尝试使用 C 语言编写的 OpenGL 和 GLUT 渲染一个简单(10x3x10 大小)的房间。

我想创建一个简单的静态聚光灯,同时我可以在房间里四处走动。

(最终目标是从天花板上的灯发出聚光灯)

问题是当我移动相机时光线会发生变化。

我正在从main() 调用以下函数:

void init() 
    glEnable(GL_TEXTURE_2D);

    loadTexture(); // loading some textures using SOIL
    loadModels(); // loading some OBJ models

    glShadeModel(GL_SMOOTH);
    glEnable(GL_NORMALIZE);
    glEnable(GL_AUTO_NORMAL);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_COLOR_MATERIAL);
    glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
    glClearDepth(1);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glDepthFunc(GL_LEQUAL);
    glClearColor(0, 0, 0, 1);

我传递给glutDisplayFunc()的函数:

void display() 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    initLight();

    gluLookAt(x, 1.5f, z, x + vX, 1.5f, z + vZ, 0.0f, 1.5f, 0.0f);

    // ...
    // drawing the ground, ceiling and the walls
    // no matrix transformations here, just simple glVertex () calls
    // ...

    glPushMatrix();

    // drawing a table
    glTranslatef(5, 0.842843, 5);
    glBindTexture(GL_TEXTURE_2D, texture[1]);
    draw_model(&modelTable);

    // drawing some chairs
    glTranslatef(0, -0.312053, chair1Position);
    glBindTexture(GL_TEXTURE_2D, texture[2]);
    draw_model(&modelChair1);
    glTranslatef(0, 0, chair2Position);
    draw_model(&modelChair2);

    glPopMatrix();

    glutSwapBuffers();

还有initLight() 函数:

void initLight() 

    // i would like the light to originate from an upper corner
    // directed to the opposing lower corner (across the room basically)

    GLfloat lightPosition[] =  10, 3, 0, 1 ;
    GLfloat lightDirection[] =  0, 0, 10, 0 ;
    GLfloat ambientLight[] =  0.3, 0.3, 0.3, 1 ;
    GLfloat diffuseLight[] =  1, 1, 1, 1 ;

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
    glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
    glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 180);
    glLightf(GL_LIGHT0, GL_SPOT_EXPONENT, 64);
    glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, lightDirection);

有人告诉我这个问题可以通过正确定位 glPushMatrix()glPopMatrix() 调用来解决。

无论我把它们放在哪里,光线要么在我移动相机时不断变化,要么根本没有光线。我就是想不通。

这里有什么合适的解决方案?

提前致谢。

编辑: 正如@derhass 建议的那样,我已将initLight() 调用移至gluLookAt() 调用之后。 当我四处走动时,光线仍在变化。

下面有两张截图。

在第一个上,光线甚至不可见。当我稍微向右转时,它出现了。

EDIT2

完整(精简)源代码:

#include <GL/glut.h>
#include <SOIL/SOIL.h>
#include <math.h>

GLfloat lightPosition[] =  10, 3, 0, 1 ;
GLfloat lightDirection[] =  0, 0, 10, 0 ;
GLfloat diffuseLight[] =  0, 1, 0, 1 ;
GLfloat ambientLight[] =  0.2, 0.2, 0.2, 1 ;

float x = 1.0f, z = 5.0f;
float vX = 1.0f, vZ = 0.0f;
float angle = 1.5f;

int windowWidth = 1024;
int windowHeight = 768;

char* textureFiles[13] =  "floortexture.png", "tabletexture.png",
        "chairtexture.png", "orange.png", "helptexture.png",
        "fridgetexture.png", "oven.png", "yellow.png", "dishwasher.png",
        "metallic.png", "cabinet.png", "wood.png", "cabinet2.png" ;
GLuint texture[13];

void loadTexture() 
    for (int i = 0; i < 13; i++) 
        texture[i] = SOIL_load_OGL_texture(textureFiles[i], SOIL_LOAD_RGBA,
                SOIL_CREATE_NEW_ID, 0);

        glBindTexture(GL_TEXTURE_2D, texture[i]);

        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    


void init() 
    glEnable(GL_TEXTURE_2D);

    loadTexture();

    glShadeModel(GL_SMOOTH);
    glEnable(GL_NORMALIZE);
    glEnable(GL_AUTO_NORMAL);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_COLOR_MATERIAL);
    glClearDepth(1);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glDepthFunc(GL_LEQUAL);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
    glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
    glClearColor(0, 0, 0, 1);


void processSpecialKeys(int key, int xx, int yy) 

    float moveFraction = 0.2f;
    float angleFraction = 0.1f;

    switch (key) 
    case GLUT_KEY_LEFT:
        angle -= angleFraction;
        vX = sin(angle);
        vZ = -cos(angle);
        break;
    case GLUT_KEY_RIGHT:
        angle += angleFraction;
        vX = sin(angle);
        vZ = -cos(angle);
        break;
    case GLUT_KEY_UP:
        x += vX * moveFraction;
        z += vZ * moveFraction;
        if (x < 1) 
            x = 1;
        
        if (z < 1) 
            z = 1;
        
        if (x > 9) 
            x = 9;
        
        if (z > 9) 
            z = 9;
        
        break;
    case GLUT_KEY_DOWN:
        x -= vX * moveFraction;
        z -= vZ * moveFraction;
        if (x < 1) 
            x = 1;
        
        if (z < 1) 
            z = 1;
        
        if (x > 9) 
            x = 9;
        
        if (z > 9) 
            z = 9;
        
        break;
    


void initLight() 
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
    glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
    glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 180);
    glLightf(GL_LIGHT0, GL_SPOT_EXPONENT, 128);
    glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, lightDirection);


void renderWalls() 
    // floor
    glBindTexture(GL_TEXTURE_2D, texture[0]);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glBegin(GL_QUADS);

    glTexCoord2f(0, 0);
    glVertex3f(0.0f, 0.0f, 0.0f);

    glTexCoord2f(0, 8);
    glVertex3f(0.0f, 0.0f, 10.0f);

    glTexCoord2f(8, 8);
    glVertex3f(10.0f, 0.0f, 10.0f);

    glTexCoord2f(8, 0);
    glVertex3f(10.0f, 0.0f, 0.0f);

    glEnd();

    // walls
    glBindTexture(GL_TEXTURE_2D, texture[3]);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    // first wall
    glBegin(GL_QUADS);

    glTexCoord2f(0, 0);
    glVertex3f(0.0f, 0.0f, 0.0f);

    glTexCoord2f(0, 1);
    glVertex3f(0.0f, 3.0f, 0.0f);

    glTexCoord2f(1, 1);
    glVertex3f(10.0f, 3.0f, 0.0f);

    glTexCoord2f(1, 0);
    glVertex3f(10.0f, 0.0f, 0.0f);

    glEnd();

    // second wall
    glBegin(GL_QUADS);

    glTexCoord2f(0, 0);
    glVertex3f(10.0f, 0.0f, 0.0f);

    glTexCoord2f(0, 1);
    glVertex3f(10.0f, 3.0f, 0.0f);

    glTexCoord2f(1, 1);
    glVertex3f(10.0f, 3.0f, 10.0f);

    glTexCoord2f(1, 0);
    glVertex3f(10.0f, 0.0f, 10.0f);

    glEnd();

    // third wall
    glBegin(GL_QUADS);

    glTexCoord2f(0, 0);
    glVertex3f(10.0f, 0.0f, 10.0f);

    glTexCoord2f(0, 1);
    glVertex3f(10.0f, 3.0f, 10.0f);

    glTexCoord2f(1, 1);
    glVertex3f(0.0f, 3.0f, 10.0f);

    glTexCoord2f(1, 0);
    glVertex3f(0.0f, 0.0f, 10.0f);

    glEnd();

    // fourth wall
    glBegin(GL_QUADS);

    glTexCoord2f(0, 0);
    glVertex3f(0.0f, 0.0f, 0.0f);

    glTexCoord2f(0, 1);
    glVertex3f(0.0f, 3.0f, 0.0f);

    glTexCoord2f(1, 1);
    glVertex3f(0.0f, 3.0f, 10.0f);

    glTexCoord2f(1, 0);
    glVertex3f(0.0f, 0.0f, 10.0f);

    glEnd();

    // ceiling
    glBindTexture(GL_TEXTURE_2D, texture[7]);
    glBegin(GL_QUADS);

    glTexCoord2f(0, 0);
    glVertex3f(0.0f, 3.0f, 0.0f);

    glTexCoord2f(0, 1);
    glVertex3f(10.0f, 3.0f, 0.0f);

    glTexCoord2f(1, 1);
    glVertex3f(10.0f, 3.0f, 10.0f);

    glTexCoord2f(1, 0);
    glVertex3f(0.0f, 3.0f, 10.0f);

    glEnd();


void display() 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    gluLookAt(x, 1.5f, z, x + vX, 1.5f, z + vZ, 0.0f, 1.5f, 0.0f);
    initLight();

    renderWalls();

    glutSwapBuffers();


void changeWindowSize(int width, int height) 
    if (height == 0) 
        height = 1;
    

    float ratio = 1.0 * width / height;

    windowWidth = width;
    windowHeight = height;

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    glViewport(0, 0, width, height);

    gluPerspective(45.0f, ratio, 0.1f, 100.0f);

    glMatrixMode(GL_MODELVIEW);


int main(int argc, char **argv) 
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowPosition(0, 0);
    glutInitWindowSize(windowWidth, windowHeight);
    glutCreateWindow("Kitchen");

    init();

    glutDisplayFunc(display);
    glutReshapeFunc(changeWindowSize);
    glutIdleFunc(display);
    glutSpecialFunc(processSpecialKeys);

    glutMainLoop();

    return 0;

【问题讨论】:

如何让相机和灯光在同一个参考系中,这样你就能得到想要的效果? @LuisColorado 感谢您的回复。我怎么能做到这一点? 【参考方案1】:

你的灯静止的 - 但对着相机。

固定功能 OpenGL 在眼睛空间中进行所有光照计算。在您指定灯光的GL_POSITION 的那一刻,它会根据调用时的当前modelView 矩阵 进行变换,以获得眼睛空间位置。由于modelView 设置为身份,它总是将灯光设置到给定的眼睛空间位置 - 无论您以后将相机放置在哪里。

要解决此问题,只需将 initLight() 移动到 gluLookAt 之后。

但是。你根本不应该这样做。您正在使用 已弃用的 OpenGL 功能,十年后 就不应使用这些功能。在现代 GL 中,该功能已完全删除。因此,如果您在 2016 年学习 OpenGL,请帮自己一个忙,忘掉那些老东西,直接学习着色器。

【讨论】:

感谢您的回复。不幸的是,在gluLookAt() 调用之后移动initLight() 不起作用。有关屏幕截图,请参阅我编辑的问题。这是一项任务,我必须使用这些已弃用的功能。如果您有任何其他想法,为什么在移动时灯光仍然会变化,请告诉我。谢谢。 嗯,目前还不清楚我们在那里实际看到的内容。 “房间”是如何建模的?法线是如何设置的?照明不独立于观察位置——镜面反射分量总是依赖于视图。使用 gouraud 着色和面部上足够低的细分,结果甚至可能是正确的。我不知道这种不规则抖动是从哪里来的。这是什么类型的 GL 实现? 我在问题末尾添加了指向源代码的 pastebin 链接。将其剥离到最低限度,以使问题可重现。如果你有时间,请看看。谢谢你。我在我的 Debian 机器上使用默认的 OpenGL 实现。 不要链接到外部代码。将相关部分直接放入问题中。但是,您的法线在该链接中完全损坏。如果不提供适当的法线,您将永远无法获得适当的照明。 哦,那些glNormal 调用不应该出现在renderWalls() 函数中。使用正确的源代码编辑了问题。还是法线的问题?如果是这样,请指出我正确的方向。谢谢。

以上是关于OpenGL:移动相机时如何使光线静止?的主要内容,如果未能解决你的问题,请参考以下文章

使聚光灯静止

如何在 C++ 中使用 sdl、opengl 移动相机时修复奇怪的相机旋转

在opengl中,如何将鼠标跟随更改为鼠标单击、拖动和释放?

如何为光线投射生成相机光线

使用Ogre3D从光标坐标导出光线

当阴影相机移动时,Opengl es 2.0阴影会闪烁