1
votes

I'm working on a project that requires two lights in opengl. These should be directional lights. I'm 99% sure I have taken a route that is more complicated than it needed to be, but I'm not understanding the project well as is, so whatever I could get to work was the route I took. I have a pyramid object with a texture on it. I also have two separate lights, a fill light and a key light. The fill light is more for the ambient lighting, and the key light is the primary "lightbulb" per se. For all 3 objects, I have a vertex shader and fragment shader (although I don't know if I actually needed to / should have done that). I have a list of verticies that draw the shapes, contain normals, and texture coordinates. The primary pyramid object uses all 3 of these attributes, but the lights only use the verticies so they have the same shape.

My main problem is that the lights aren't working correctly. The pyramid is only lit via the ambient lighting of the key light. It doesn't properly incorporate the diffuse or specular lighting components. The fill light also seems to be largely ignored, but it seems to be because the key light is overriding all other light data.

Here is what my shaders look like for the primary pyramid:

/* Pyramid Vertex Shader Source Code */
const GLchar * pyramidVertexShaderSource = GLSL(330,
    layout (location = 0) in vec3 position; // Vertex data from Vertex Attrib Pointer 0
    layout (location = 1) in vec3 normal; // VAP position 1 for normals
    layout (location = 2) in vec2 textureCoordinate; // Color data from Vertex Attrib Pointer 1

    out vec3 Normal; // For outgoing normals to fragment shader
    out vec3 FragmentPos; // For outgoing color / pixels to fragment shader
    out vec2 mobileTextureCoordinate; // variable to transfer Texture data to the fragment shader


    // Global variables for the transform matrices
    uniform mat4 model;
    uniform mat4 view;
    uniform mat4 projection;

void main(){
        gl_Position = projection * view * model * vec4(position, 1.0f); // transforms vertices to clip coordinates
        FragmentPos = vec3(model * vec4(position, 1.0f)); // Gets fragment / pixel position in world space only (exclude view and projection)
        mobileTextureCoordinate = vec2(textureCoordinate.x, 1.0f - textureCoordinate.y); // flips the texture horizontal
        Normal = mat3(transpose(inverse(model))) * normal; // get normal vectors in world space only and exclude normal translation properties
    }
);


/* Pyramid Fragment Shader Source Code */
const GLchar * pyramidFragmentShaderSource = GLSL(330,

        in vec2 mobileTextureCoordinate; // Variable to hold incoming color data from vertex shader
        in vec3 Normal; // For incoming normals
        in vec3 FragmentPos; // For incoming fragment position

        out vec4 pyramidColor; // For outgoing pyramid color


        // Uniform variables for object color, light color, light position, and camera/view position
        uniform vec3 fillLightColor;
        uniform vec3 fillLightPos;
        uniform vec3 keyLightColor;
        uniform vec3 keyLightPos;
        uniform vec3 viewPosition;
        uniform sampler2D uTexture; // Useful when working with multiple textures

        void main() {
            /* Phong lighting model calculations to generate ambient, diffuse, and specular components */

            // Calculate Ambient lighting
            float ambientStrength = 0.3f; // Set ambient or global lighting strength
            vec3 ambient = ambientStrength * fillLightColor; // Generate ambient light color

            // Calculate Diffuse lighting for fill lamp
            vec3 norm = normalize(Normal); // Normalize vectors to 1 unit
            vec3 lightDirection = normalize(fillLightPos - FragmentPos); // Calculate distance (light direction) between fill light source and fragments/pixels
            float impact = max(dot(norm, lightDirection), 0.0); // Calculate diffuse impact by generating dot product of normal and light
            vec3 diffuse = impact * fillLightColor; // Generate diffuse light color

            // Calculate Specular lighting for fill lamp
            float specularIntensity = 0.8f; // Set specular light strength
            float highlightSize = 16.0f; // Set specular highlight size
            vec3 viewDir = normalize(viewPosition - FragmentPos); // Calculate view direction
            vec3 reflectDir = reflect (-lightDirection, norm); // Calculate reflection vector
            //Calculate specular component
            float specularComponent = pow(max(dot(viewDir, reflectDir), 0.0), highlightSize);
            vec3 specular = specularIntensity * specularComponent * fillLightColor;

            // Calculate Ambient lighting for key lamp
            float keyAmbientStrength = 1.0f;
            vec3 keyAmbient = keyAmbientStrength * keyLightColor;

            // Calculate Diffuse lighting for key lamp
            vec3 keyLightDirection = normalize(keyLightPos - FragmentPos); // Calculate distance between key light source and fragments
            float keyImpact = max(dot(norm, keyLightDirection), 0.0);
            vec3 keyDiffuse = keyImpact * keyLightColor;

            // Calculate Specular lighting for key lamp
            float keySpecularIntensity = 0.8f; // Set specular light strength
            float keyHighlightSize = 32.0f; // Set specular highlight size
            vec3 keyReflectDir = reflect (-keyLightDirection, norm); // Calculate reflection vector
            //Calculate specular component
            float keySpecularComponent = pow(max(dot(viewDir, keyReflectDir), 0.0), keyHighlightSize);
            vec3 keySpecular = keySpecularIntensity * keySpecularComponent * keyLightColor;

            // Calculate phong result
            vec3 objectColor = texture(uTexture, mobileTextureCoordinate).xyz;
            vec3 fillResult = (ambient + diffuse + specular);
            vec3 keyResult = (keyAmbient + keyDiffuse + keySpecular);
            vec3 lightingResult = fillResult + keyResult;
            vec3 phong = (lightingResult) * objectColor;
            pyramidColor = vec4(phong, 1.0f); // Send lighting results to GPU

        }
    ); 

As far as I can tell, my issue is in my mathematics for calculating the lighting, but no matter how I try to change it... I'm just not smart enough to know what exactly is going on. Admittedly my linear algebra skills are garbage so I'm really having a hard time. I'm sure there are many errors in my code and how I approached the issue, but after probably upwards of 15-20 hours of trying, this is the best I could manage to load and look even remotely correct.

The fill light is pretty much supposed to be a white light, illuminating the the texture as a normal light would. The key light is supposed to be green, so the side(s) that are exposed to the key light should have a green look to them. Currently, the whole pyramid is just illuminated in the same greenish color (no shading or directional appearances to the lights).

2

2 Answers

1
votes

On a first gaze your code looks correct. Of course the fragment color depends on the light color and the object (texture) color. It is common to multiply (modulate) the object color and the light color. Another possibility is to sum the object color and the light color. Anyway that is not the issue in your shader code.

But there a lot of possible issues. Ensure that all uniforms are proper set. Ensure that the light source is not "behind" the object and that the light colors are not "zero".

An often seen issue is that the point light source is located in the mesh. In that case, the inside of the object is illuminated (which you can't see), but the outside is on the shadow side.

A common issue is that the normal vector of the surface points to the inside the mesh. THis will cause no diffuse and specular light at all, because the Dot product computes a value which is proportional to the cosine of the angle between the 2 vectors. If the angle between the 2 vectors is grater than 90° the the cosine becomes negative and thus the Dot product is negative, too.

You can get rid of that by implementing a double sided light model and inverting the normal vector if it doesn't face the view. Do the following at the begin of the fragment shader:

vec3 norm    = normalize(Normal);
vec3 viewDir = normalize(viewPosition - FragmentPos);

if (dot(norm, viewDir) < 0.0)
    norm = -norm;

Anyway that won't help anything, if the normal vector attribute is not proper specified and all the normal vectors are (0, 0, 0).
To evaluate the normal vectors you can do a simple debug test. Just add the normal vector to pyramidColor in the last line of the fragment shader:

void main() {
    // [...]

    pyramidColor.rgb += abs(norm.xyz);
}
1
votes

This is my first answer on Stack so I'd like to take it step by step. Clearly you're using the LearnOpenGL tutorial (might be wrong but the code looks familiar) I just got over elementary lighting problems with some help from this tutorial, I'm a beginner as well but hope to provide some help.

Vertex Shader:

So, for your Vertex Shader, you take in 3 'attrib' pointers: the position, normal, and texture coordinate - this is standard and I expect you're buffering/ passing the data in correctly.

layout (location = 0) in vec3 position; 
layout (location = 1) in vec3 normal; 
layout (location = 2) in vec2 textureCoordinate;

That's no problem and I can't see how it could be (without seeing the rest of your code).

out vec3 Normal;
out vec3 FragmentPos; 
out vec2 mobileTextureCoordinate; 

You also pass the vertex normal, the fragment position, and the texture coordinate to the fragment shader (by the way - why do you call it a 'Mobile Texture Coordinate'? I'm not well-versed enough to guess); it all looks fine up to here: no problems.

Then you have your uniforms: the model, view, and projection matrix declarations - sure, no problem.

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

Now into the main function of your vertex shader. It's probably not here that the problem originates, because apparently your pyramid renders correctly - only with improper lighting.

void main(){
        gl_Position = projection * view * model * vec4(position, 1.0f); 

Looks good.

    FragmentPos = vec3(model * vec4(position, 1.0f)); 

Looks good, you ignore the projection matrix and obviously the view matrix.

    mobileTextureCoordinate = vec2(textureCoordinate.x, 1.0f - textureCoordinate.y);

You flip your texture coordinate horizontally (no idea why yet - but whatever)

        Normal = mat3(transpose(inverse(model))) * normal; 
    }
);

Don't fully understand what's going on here, but not about to try to hash it out - you are probably annulling some transformations so that the normals are still valid independent of model transformations or something (I don't really know if this is correct and hope someone can edit my response.)

Fragment Shader:

    in vec2 mobileTextureCoordinate; 
    in vec3 Normal; 
    in vec3 FragmentPos; 
    out vec4 pyramidColor; // For outgoing pyramid color

No problem so far

    uniform vec3 fillLightColor;
    uniform vec3 fillLightPos;
    uniform vec3 keyLightColor;
    uniform vec3 keyLightPos;
    uniform vec3 viewPosition;
    uniform sampler2D uTexture; 

Assuming these uniforms are set properly, and you are calling a glUseProgram()/ "shader".useProgram()/ binding the shader equivalent before setting the uniforms with glUniformX calls

    void main() {

        // Calculate Ambient lighting
        float ambientStrength = 0.3f; 
        vec3 ambient = ambientStrength * fillLightColor; 

        // Calculate Diffuse lighting for fill lamp
        vec3 norm = normalize(Normal); // Normalize vectors to 1 unit
        vec3 lightDirection = normalize(fillLightPos - FragmentPos); // Calculate distance (light direction) between fill light source and fragments/pixels
        float impact = max(dot(norm, lightDirection), 0.0); // Calculate diffuse impact by generating dot product of normal and light
        vec3 diffuse = impact * fillLightColor; // Generate diffuse light color

        // Calculate Specular lighting for fill lamp
        float specularIntensity = 0.8f; // Set specular light strength
        float highlightSize = 16.0f; // Set specular highlight size
        vec3 viewDir = normalize(viewPosition - FragmentPos); // Calculate view direction
        vec3 reflectDir = reflect (-lightDirection, norm); // Calculate reflection vector
        //Calculate specular component
        float specularComponent = pow(max(dot(viewDir, reflectDir), 0.0), highlightSize);
        vec3 specular = specularIntensity * specularComponent * fillLightColor;

        // Calculate Ambient lighting for key lamp
        float keyAmbientStrength = 1.0f;
        vec3 keyAmbient = keyAmbientStrength * keyLightColor;

        // Calculate Diffuse lighting for key lamp
        vec3 keyLightDirection = normalize(keyLightPos - FragmentPos); // Calculate distance between key light source and fragments
        float keyImpact = max(dot(norm, keyLightDirection), 0.0);
        vec3 keyDiffuse = keyImpact * keyLightColor;

        // Calculate Specular lighting for key lamp
        float keySpecularIntensity = 0.8f; // Set specular light strength
        float keyHighlightSize = 32.0f; // Set specular highlight size
        vec3 keyReflectDir = reflect (-keyLightDirection, norm); // Calculate reflection vector
        //Calculate specular component
        float keySpecularComponent = pow(max(dot(viewDir, keyReflectDir), 0.0), keyHighlightSize);
        vec3 keySpecular = keySpecularIntensity * keySpecularComponent * keyLightColor;

        // Calculate phong result
        vec3 objectColor = texture(uTexture, mobileTextureCoordinate).xyz;
        vec3 fillResult = (ambient + diffuse + specular);
        vec3 keyResult = (keyAmbient + keyDiffuse + keySpecular);
        vec3 lightingResult = fillResult + keyResult;
        vec3 phong = (lightingResult) * objectColor;
        pyramidColor = vec4(phong, 1.0f); // Send lighting results to GPU

    }
); 

Have you made sure that pyramidColor is truly setting the color of the fragment?

It all looks cookie cutter from there on in but I haven't tested myself!