openglglslphongspecular

Strange specular results from GLSL phong shader


I'm trying to implement phong shading in GLSL but am having some issues with the specular component.

Strange specular result

The green light is the specular component. The light (a point light) travels in a circle above the plane. The specular highlight always points inward toward the Y axis about which the light rotates and fans out toward the diffuse reflection as seen in the image. It doesn't appear to be affected at all by the positioning of the camera and I'm not sure where I'm going wrong.

Vertex shader code:

#version 330 core

/*
* Phong Shading with with Point Light (Quadratic Attenutation)
*/

//Input vertex data
layout(location = 0) in vec3 vertexPosition_modelSpace;
layout(location = 1) in vec2 vertexUVs;
layout(location = 2) in vec3 vertexNormal_modelSpace;

//Output Data; will be interpolated for each fragment
out vec2 uvCoords;

out vec3 vertexPosition_cameraSpace;
out vec3 vertexNormal_cameraSpace;

//Uniforms
uniform mat4 mvMatrix;
uniform mat4 mvpMatrix;
uniform mat3 normalTransformMatrix;


void main()
{
    vec3 normal = normalize(vertexNormal_modelSpace);

    //Set vertices in clip space
    gl_Position = mvpMatrix * vec4(vertexPosition_modelSpace, 1);

    //Set output for UVs
    uvCoords = vertexUVs;

    //Convert vertex and normal into eye space
    vertexPosition_cameraSpace = mat3(mvMatrix) * vertexPosition_modelSpace;    
    vertexNormal_cameraSpace = normalize(normalTransformMatrix * normal);

}

Fragment Shader Code:

#version 330 core

in vec2 uvCoords;

in vec3 vertexPosition_cameraSpace;
in vec3 vertexNormal_cameraSpace;

//out
out vec4 fragColor;

//uniforms
uniform sampler2D diffuseTex;
uniform vec3 lightPosition_cameraSpace;

void main()
{
    const float materialAmbient = 0.025;    //a touch of ambient

    const float materialDiffuse = 0.65;
    const float materialSpec = 0.35;  

    const float lightPower = 2.0;
    const float specExponent = 2;

    //--------------Set Colors and determine vectors needed for shading-----------------           

    //reflection colors- NOTE- diffuse and ambient reflections will use the texture color
    const vec3 colorSpec = vec3(0,1,0); //Green spec color
    vec3 diffuseColor = texture2D(diffuseTex, uvCoords).rgb;    //Get color from the texture at fragment

    const vec3 lightColor = vec3(1,1,1);        //White light

    //Re-normalize normal vectors : after interpolation they make not be unit length any longer
    vec3 normVertexNormal_cameraSpace = normalize(vertexNormal_cameraSpace);

    //Set camera vec
    vec3 viewVec_cameraSpace = normalize(-vertexPosition_cameraSpace);  //Since its view space, camera at origin

    //Set light vec
    vec3 lightVec_cameraSpace = normalize(lightPosition_cameraSpace - vertexPosition_cameraSpace);

    //Set reflect vect
    vec3 reflectVec_cameraSpace = normalize(reflect(-lightVec_cameraSpace, normVertexNormal_cameraSpace));      //reflect function requires incident vec; from light to vertex

    //----------------Find intensity of each component---------------------

    //Determine Light Intensity
    float distance = abs(length(lightPosition_cameraSpace - vertexPosition_cameraSpace));
    float lightAttenuation = 1.0/( (distance > 0) ? (distance * distance) : 1 );    //Quadratic

    vec3 lightIntensity = lightPower * lightAttenuation * lightColor;

    //Determine Ambient Component
    vec3 ambientComp = materialAmbient * diffuseColor * lightIntensity;

    //Determine Diffuse Component
    float lightDotNormal = max( dot(lightVec_cameraSpace, normVertexNormal_cameraSpace), 0.0 );
    vec3 diffuseComp = materialDiffuse *  diffuseColor * lightDotNormal * lightIntensity;

    vec3 specComp = vec3(0,0,0);
    //Determine Spec Component
    if(lightDotNormal > 0.0)
    {
        float reflectDotView = max( dot(reflectVec_cameraSpace, viewVec_cameraSpace), 0.0 );
        specComp = materialSpec *  colorSpec * pow(reflectDotView, specExponent) * lightIntensity;
    }

    //Add Ambient + Diffuse + Spec
    vec3 phongFragRGB = ambientComp + 
          diffuseComp + 
          specComp;  

    //----------------------Putting it together-----------------------

    //Out Frag color
    fragColor = vec4(  phongFragRGB, 1);
}

Just noting that the normalTransformMatrix seen in the Vertex shader is the inverse-transpose of the model-view matrix.

I am setting a vector from the vertex position to the light, to the camera, and the reflect vector, all in camera space. For the diffuse calculation I am taking the dot product of the light vector and the normal vector, and for the specular component I am taking the dot product of the reflection vector and the view vector. Perhaps there is some fundamental misunderstanding that I have with the algorithm?

I thought at first that the problem could be that I wasn't normalizing the normals entering the fragment shader after interpolation, but adding a line to normalize didn't affect the image. I'm not sure where to look.

I know that there a lot of phong shading questions on the site, but everyone seems to have a problem that is a bit different. If anyone can see where I am going wrong, please let me know. Any help is appreciated.

EDIT: Okay its working now! Just as jozxyqk suggested below, I needed to do a mat4*vec4 operation for my vertex position or lose the translation information. When I first made the change I was getting strange results until I realized that I was making the same mistake in my OpenGL code for the lightPosition_cameraSpace before I passed it to the shader (the mistake being that I was casting down the view matrix to a mat3 for the calculation instead of setting the light position vector as a vec4). Once I edited those lines the shader appears to be working properly! Thanks for the help, jozxqk!


Solution

  • I can see two parts which don't look right.

    1. "vertexPosition_cameraSpace = mat3(mvMatrix) * vertexPosition_modelSpace" should be a mat4/vec4(x,y,z,1) multiply, otherwise it ignores the translation part of the modelview matrix.

    2. distance uses the light position relative to the camera and not the vertex. Use lightVec_cameraSpace instead. (edit: missed the duplicated calculation)