Search code examples
androidopengl-esglslshaderlighting

opengl-es pre pixel lighting issue


there is a problem that i just can't seem to get a handle on.. i have a fragment shader:

precision mediump float;

uniform vec3 u_AmbientColor;
uniform vec3 u_LightPos;
uniform float u_Attenuation_Constant;
uniform float u_Attenuation_Linear;
uniform float u_Attenuation_Quadradic;
uniform vec3 u_LightColor;

varying vec3 v_Normal;
varying vec3 v_fragPos;

vec4 fix(vec3 v);

void main() {

    vec3 color = vec3(1.0,1.0,1.0);

    vec3 vectorToLight = u_LightPos - v_fragPos;
    float distance = length(vectorToLight);
    vec3 direction = vectorToLight / distance;
    float attenuation = 1.0/(u_Attenuation_Constant + 
    u_Attenuation_Linear * distance + u_Attenuation_Quadradic * distance * distance);
    vec3 diffuse = u_LightColor * attenuation * max(normalize(v_Normal) * direction,0.0);
    vec3 d = u_AmbientColor + diffuse;
    gl_FragColor = fix(color * d);
}
vec4 fix(vec3 v){
    float r = min(1.0,max(0.0,v.r));
    float g = min(1.0,max(0.0,v.g));
    float b = min(1.0,max(0.0,v.b));
    return vec4(r,g,b,1.0);
}

i've been following some tutorial i found on the web, anyways, the ambientColor and lightColor uniforms are (0.2,0.2,0.2), and (1.0,1.0,1.0) respectively. the v_Normal is calculated at the vertex shader using the inverted transposed matrix of the model-view matrix.

the v_fragPos is the model result of multiplying the position with the normal model-view matrix.

now, i expect that when i move the light position closer to the cube i render, it will just appear brighter, but the resulting image is very different:

enter image description here

(the little square there is an indicator for the light position) now, i just don't understand how this can happen? i mean, i multiply the color components each by the SAME value.. so, how is it that it seems to vary so??

EDIT: i noticed that if i move the camera in front of the cube, the light is just shades of blue.. which is the same problem but maybe it's a clue i don't know..


Solution

  • The Lambertian reflectance is computed with the Dot product of the normal vector and the vector to the light source, instead of the component wise product.
    See How does the calculation of the light model work in a shader program?

    Use the dot function instead of the * (multiplication) operator:

    vec3 diffuse = u_LightColor * attenuation * max(normalize(v_Normal) * direction,0.0);

    vec3 diffuse = u_LightColor * attenuation * max(dot(normalize(v_Normal), direction), 0.0);
    

    You can simplify the code in the fix function. min and max can be substituted with clamp. This functions work component wise, so they do not have to be called separately for each component:

    vec4 fix(vec3 v)
    {
        return vec4(clamp(v, 0.0, 1.0), 1.0);
    }