Question

I'm writing a shader that should implement the Phong lighting model. The problem is that I am not using glRotatef and these functions, instead I am passing a scale and translate vector, and a rotation matrix to the shader. I draw a teapot on the screen, and it looks ok, except when I rotate it. The light doesn't seem to move, it's like if the light is wired to the teapot and it rotates along.

Vertex shader:

#version 120

varying vec3 normal, position;
uniform vec3 scale, translation;
uniform mat4 rotation;

void main()
{
    vec4 vertex= (gl_Vertex * rotation) * vec4(scale,1.0) + vec4(translation,0.0);
    gl_Position= gl_ProjectionMatrix * gl_ModelViewMatrix *vertex ;
    normal= normalize(gl_NormalMatrix * gl_Normal);
    position= vec3(gl_ModelViewMatrix * gl_Vertex);
}

Fragment shader:

#version 120

#pragma optionNV( unroll none)

uniform int model;
// 1: phong, 2: blinn-phong
varying vec3 normal, position;
// 1: directional, 2:point, 3:spot
uniform int lightType;
uniform float spotExponent;


vec4 phong(vec3 L, vec3 N, vec3 V, int light) {
    float LdotN= max(dot(L,N),0.0);
    vec3 R= 2.0 * LdotN * N - L;
    vec4 color= gl_FrontMaterial.ambient * gl_LightSource[light].ambient;
    color+= gl_FrontMaterial.diffuse * gl_LightSource[light].diffuse * LdotN;
    color+= gl_FrontMaterial.specular * gl_LightSource[light].specular * pow(max(dot(R,V),0.0),gl_FrontMaterial.shininess);
    return color;
}

vec4 blinnPhong(vec3 L, vec3 N, vec3 V, int light) {
    float LdotN= max(dot(L,N),0.0);
    vec3 H= normalize(L+V);
    vec4 color= gl_FrontMaterial.ambient * gl_LightSource[light].ambient;
    color+= gl_FrontMaterial.diffuse * gl_LightSource[light].diffuse * LdotN;
    color+= gl_FrontMaterial.specular * gl_LightSource[light].specular * pow(max(dot(H,N),0.0),gl_FrontMaterial.shininess);
    return color;
}

float norm(vec3 v) {
    return sqrt(v.x*v.x + v.y*v.y + v.z*v.z);
}

float pointLightAttenuation(float range, int light) {
    float distance= norm(gl_LightSource[light].position.xyz - position);
    if(distance > range)
        return 0.0;
    else
        return max(1.0,1.0/(gl_LightSource[light].quadraticAttenuation*distance*distance+gl_LightSource[light].linearAttenuation*distance+gl_LightSource[light].constantAttenuation));
}

float spotLightAttenuation(vec3 L, int light)  
{
    //return pow(max(dot(normalize(gl_LightSource[light].spotDirection),-L),0.0),spotExponent);
    return max(dot(normalize(gl_LightSource[light].spotDirection),-L),0.0);
}


void main()
{
    vec3 N= normalize(normal);
    vec3 L;
    float attenuation= 1.0;
    if(lightType== 1) { 
        // If light is directional
        L= normalize(- position);
    }
    else {
        L= normalize(gl_LightSource[0].position.xyz - position);
        attenuation= pointLightAttenuation(90.0,0);
        if(lightType== 3)
            attenuation*= spotLightAttenuation(L,0);
    }
    vec3 V= -normalize(position);
    if(model==1)
        gl_FragColor= attenuation * phong(L,N,V,0);
    else
        gl_FragColor= attenuation * blinnPhong(L,N,V,0);
}

I already debugged the shader, and I think there's no problem in the output calculation, except for the fact that it doesn't take into account the object rotation. So I suggest to don't focus too much on the Phong equation computation.

This is a screenshot of the teapot drawn before the rotation:

enter image description here

And this one after a rotation around the Y axis:

enter image description here

Like you see it's like if the light moves along with the teapot, but the light is at a fixed position: always (0,0,-15) !

Was it helpful?

Solution

The problem is that I am not using glRotatef and these functions, instead I am passing a scale and translate vector, and a rotation matrix to the shader.

Yeah, but then you only use those to compute the vertex's clip-space position. The values that you use for computing the lighting (after putting them into varyings and getting the interpolated values in the fragment shader) are still computed using the old builtin matrices only and thus your lighting computations are not transformed in the same way as your geometry is.

So just change

normal= normalize(gl_NormalMatrix * gl_Normal);
position= vec3(gl_ModelViewMatrix * gl_Vertex);

to

normal = normalize(gl_NormalMatrix * ((gl_Normal * mat3(rotation)) / scale));
position = vec3(gl_ModelViewMatrix * vertex);

But to be honest, you are far better off by just computing a proper matrix from your three parameters and multiplying that onto the modelview matrix on the CPU before drawing anything, since you're still using the gl_ModelViewMatrix anyway to compute the vertex position (wether that is necessary given that you don't seem to set it to any values is a different question, though). And I'll also ignore for now that your multiplication with the rotation matrix is actually opposite the order that OpenGL normally uses (and that you use yourself with the other matrices), assuming that you are aware of this and know what you're doing.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top