glLight issues

Started by RiverC, March 27, 2012, 13:51:07

Previous topic - Next topic

RiverC

So here's somewhat of a pickle. How do you get GL_Light to work the way we expect, like the way lights work in a game?

I'm not expecting any answers, since I've seen this same question before unanswered, which tells me I'm probably out of my league intellectually at this point -- that is to say, I've missed the obvious and who can explain it? So here goes.

I would like to make a directional light that shines continually downward on the scene on any face that is not obstructed vertically by another face. As the player moves around the world, this light should act as starlight or skylight - the general luminance created by either the sunlight scattering through the atmosphere (day) or the background light of stars (night).

From my understanding, I can do something like this with GL_LIGHT, but my attempts to follow tutorials have mostly resulted in unusual and unhelpful results.

My first step is to set up a directional light like so

    
    FloatBuffer lightAmbient = BufferUtils.createFloatBuffer(4).put(new float[] {0f, 0f, 0f, 1f });
    FloatBuffer lightDiffuse = BufferUtils.createFloatBuffer(4).put(new float[] {0.6f, 0.6f, 0.8f, 1f });
    FloatBuffer lightPosition = BufferUtils.createFloatBuffer(4).put(new float[] {0f, 0f, 1f, 0f });
    
    ByteBuffer temp = ByteBuffer.allocateDirect(16);
    temp.order(ByteOrder.nativeOrder());

    lightAmbient.flip();
    glLight(GL_LIGHT0, GL_AMBIENT, (FloatBuffer)temp.asFloatBuffer().put(lightAmbient).flip());
    lightDiffuse.flip();
    glLight(GL_LIGHT0, GL_DIFFUSE, (FloatBuffer)temp.asFloatBuffer().put(lightDiffuse).flip());
    lightPosition.flip();
    glLight(GL_LIGHT0, GL_POSITION,(FloatBuffer)temp.asFloatBuffer().put(lightPosition).flip());

    glEnable(GL_COLOR_MATERIAL);
    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);


This is done every frame on render, when we switch to rendering the 3d part of our scene.

Then we attach our shaders; we attach the light-enabled shaders for the 3d part of our scene:

        if(useShader&&useLighting) {
            ARBShaderObjects.glUseProgramObjectARB(shader);
            shaderProgram = shader;
        }
        else if(useShader&&!useLighting) {
            ARBShaderObjects.glUseProgramObjectARB(basicShader);
            shaderProgram = basicShader;
        }

        int offsetLocation = ARBShaderObjects.glGetUniformLocationARB(shaderProgram, "pos");
        //System.out.println(offsetLocation);
        ARBShaderObjects.glUniform3fARB(offsetLocation, movex, movez, movey);
        int rotateLocation = ARBShaderObjects.glGetUniformLocationARB(shaderProgram, "rot");
        //System.out.println(rotateLocation);
        ARBShaderObjects.glUniform3fARB(rotateLocation, scenePitch, sceneYaw, sceneRoll);
        int textureLocation = ARBShaderObjects.glGetUniformLocationARB(shaderProgram, "theTex");
        ARBShaderObjects.glUniform1iARB(textureLocation, 0);


Next, we draw the quads for the scene:

        GL11.glTexCoord2f(origin.GLx(), extent.GLz());
        GL11.glVertex3f(origin.GLx(), origin.GLy(), extent.GLz());
        GL11.glTexCoord2f(origin.GLx(), origin.GLz());
        GL11.glVertex3f(origin.GLx(), origin.GLy(), origin.GLz());
        GL11.glTexCoord2f(extent.GLx(), origin.GLz());
        GL11.glVertex3f(extent.GLx(), origin.GLy(), origin.GLz());
        GL11.glTexCoord2f(extent.GLx(), extent.GLz());
        GL11.glVertex3f(extent.GLx(), origin.GLy(), extent.GLz());



that's the code for rendering the top face of a simple solid defined by two points. We also render all six others in similar fashion for each solid in the world.

Now, in the vertex shader we rotate the scene and render the lights:

uniform vec3 pos;
uniform vec3 rot;

void main()
{
    vec3 lightDir, normal;
    vec4 diffuse, ambient, globalAmbient;
    float NdotL;
    mat4x4 position=mat4x4(1.0);
    position[3].x=pos.x;
    position[3].y=pos.y;
    position[3].z=pos.z;
    mat4x4 heading=mat4x4(1.0);
    heading[0][0]=cos(rot.y);
    heading[0][2]=-(sin(rot.y));
    heading[2][0]=sin(rot.y);
    heading[2][2]=cos(rot.y);
    mat4x4 pitch=mat4x4(1.0);
    pitch[1][1]=cos(rot.x);
    pitch[1][2]=sin(rot.x);
    pitch[2][1]=-(sin(rot.x));
    pitch[2][2]=cos(rot.x);
    mat4x4 roll=mat4x4(1.0);
    roll[0][0]=cos(rot.z);
    roll[0][1]=sin(rot.z);
    roll[1][0]=-(sin(rot.z));
    roll[1][1]=cos(rot.z);

    // Transforming The Vertex
    gl_Position= gl_ModelViewProjectionMatrix*roll*pitch*heading*position*gl_Vertex;

    
    gl_TexCoord[0] = gl_MultiTexCoord0;

    normal = normalize(gl_NormalMatrix * gl_Normal);
    lightDir = normalize(vec3(gl_LightSource[0].position));
    NdotL = max(dot(normal, lightDir), 0.0);
    diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;

    ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
    globalAmbient = gl_LightModel.ambient * gl_FrontMaterial.ambient;

    gl_FrontColor = NdotL * diffuse + globalAmbient + ambient;


}


Lastly we do the fragment shading to get the final color of the fragment (no light should result in pitch-black, though typically it doesn't)

uniform sampler2D theTex;

void main()
{
    // Calculating The Final Color
    gl_FragColor = texture2D(theTex, gl_TexCoord[0].st) * gl_Color;
}


The result of this is a scene that is a kind of moonlight blue all over, on all faces in all directions. If I try moving the quote-unquote directional light (if that is really what is going on - I think some of this stuff is improperly documented) it dims it, but does not cause it to be 'directional'.

For instance if I make the 'position' to be

(0.0f,4.0f,1.0f,0.0f)

the light does not change direction, but instead the lighting of all surfaces gets dimmer.

Anyone who has done lighting have any ideas of what I'm doing wrong? Am I missing code for the quads? Do I need to translate the light 'position' the same way I translate the object vertices?

If this continues I'll simply have to concoct a way to do lighting without GL, since it seems like (at the moment) it doesn't do what I need lighting to do.

RiverC

As it turns out,

glNormal3f() needs to be called before creating each vertex. This solves the problem of the light being 'universal':

Quote18.020 Why are my objects all one flat color and not shaded and illuminated?

    This effect occurs when you fail to supply a normal at each vertex.

    OpenGL needs normals to calculate lighting equations, and it won't calculate normals for you (with the exception of evaluators). If your application doesn't call glNormal*(), then it uses the default normal of (0.0, 0.0, 1.0) at every vertex. OpenGL will then compute the same, or nearly the same, lighting result at each vertex. This will cause your model to look flat and lack shading.

    The solution is to simply calculate the normals that need to be specified at any given vertex. Then send them to OpenGL with a call to glNormal3f() just prior to specifying the vertex, which the normal is associated with.

    If you don't know how to calculate a normal, in most cases you can do it simply with a vector cross product. The OpenGL Programming Guide contains a small section explaining how to calculate normals. Also most basic 3D computer graphics books cover it, because it's not OpenGL-specific.

This explains part of it! It is considered to be so 'basic' that the tutorials don't cover it. Whoops!

However, ultimately, I won't be using GL_LIGHT (most likely) since it doesn't provide anything other than eight uniform inputs. However, I'll still need to precalculate, store and provide the normals... is there any utility (GLU) functions for this purpose that are optimized?