Search code examples
c++opengllightingopengl-compat

How to stop positional light moving with camera


When I rotate and/or move the camera in my openGL project, it is almost as if there is a spotlight moving with it, however I have set up my gl light to be positional and given it a static position.

void Lighting::Display()
{
    glPushMatrix();

    glTranslatef(0.f, yoffset, 0.f); // move to start
    glTranslatef(0.f, ceilHeight * scale[0], 0.f);
    DrawLight();

    glDisable(GL_LIGHTING);

    glPushAttrib(GL_ALL_ATTRIB_BITS);
    // Match colour of sphere to diffuse colour of light
    glColor4fv(specular);
    glTranslatef(0.f, -10.0f * scale[1], 0.f);
    glutSolidSphere(5.0f * scale[0], 25, 25);

    glPopAttrib();
    glPopMatrix();
    // Re-enable lighting after light source has been drawn
    glEnable(GL_LIGHTING);

    // Set properties GL_LIGHT0
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
    //glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.0001f);

    GLfloat pos[4] = { 0.f, 950.f, 0.f, 1.f };
    glLightfv(GL_LIGHT0, GL_POSITION, pos);

    // enable GL_LIGHT0 with these defined properties
    glEnable(GL_LIGHT0);
}

I expected to have a single source of light hanging in the centre of the scene, with light being emitted equally in all directions from its position however a trail of light seems to be emitted as a spotlight instead.

Here is an image showcasing the issue:

As you can see there is an odd line of light being emitted.


Solution

  • When the light position is set by glLightfv(GL_LIGHT0, GL_POSITION, pos), then pos is multiplied by the current model view matrix.

    The intensity of the ambient light (Ia), diffuse light (Id) and specular light (Is), of the Blinn–Phong reflection model is calculated as follows:

    N  ... norlmal vector 
    L  ... light vector (from the vertex position to the light) 
    V  ... view vector (from the vertex position to the eye)
    sh ... shininess
    
    H     = normalize(V + L)
    NdotH = max(dot(N, H), 0.0)
    
    Ia    = 1
    Id    = max(dot(N, L), 0.0)
    Is    = pow(NdotH, sh)
    

    So the ambient light (GL_AMBIENT) is independent of any direction.

    The diffuse light (GL_DIFFUSE) depends on the normal vector of the surface (N) and the direction of the incident light (L). It stays constant on the lit surface, independent on the point of view.

    The specular light (GL_SPECULAR) depends on the surfaces normal vector (N), the light direction (L). and the viewing direction (V). This causes that the specular highlights change, when you move in the scene, because the viewing direction to the lit surfaces changes.

    Further note that the light calculations in the deprecated OpenGL fixed function light model are done per vertex (Gouraud Shading). The calculated light is interpolated on the area, between the the corners of the primitives. A modern implementation would be to do the light calculation per fragment (Phong shading). Gouraud Shading cause the spotted look of the specular highlights at the ceiling and may increase an unexpected look. Tessellating the areas in smaller peaces may improve that, but the best solution would be to switch to modern OpenGL write a Shader and implement a per fragment light model for your needs.