Can't get GLSL per-pixel lighting to work.

I’m trying to implement per-pixel lighting with GLSL in my program and I just can’t get it to work. Also, sorry for the long post.

Every face has the same color. This image of an ugly skull that I rendered with my shaders should explain the problem:

[spoiler]

[/spoiler]

Here’s my shaders. I removed everything unrelated.

Vertex shader

#version 130

in vec3 inPosition;
in vec4 inColor;
in vec3 inNormal;

smooth out vec4 color;
smooth out vec3 normal;
smooth out vec3 position;

uniform mat4 perspectiveMatrix;
uniform mat4 cameraTransformationMatrix;
uniform mat4 modelTransformationMatrix;

uniform vec3 lightPos;

void main()
{

	normal = inNormal;
	
	position = inPosition;
	
	color = inColor;
	
	vec4 pos  = cameraTransformationMatrix * vec4(inPosition, 1);
	
    gl_Position = perspectiveMatrix * pos;
  
}

Fragment shader:

#version 130

smooth in vec4 color;
smooth in vec3 normal;
smooth in vec3 position;

uniform vec3 lightPos;

void main()
{

	vec3 lightDir = normalize(lightPos - position);

	float diffuseLightIntensity = max(0,dot(normalize(normal),lightDir));

	vec4 fragcolor = vec4(diffuseLightIntensity * color.rgb,color.a);
	
	fragcolor.rgb += vec3(0.2,0.2,0.2);

    gl_FragColor = fragcolor;
}

I’ve been trying to figure out what could cause this to happen. I’ve tried to change gl_FragColor to different things. For example, gl_FragColor = vec4(lightDir,1) draws the skull with interpolated colors (check image below).

[spoiler]

[/spoiler]

This shows that lightDir varies with every fragment. So that can’t be the problem. Now, if i put gl_FragColor = vec4(normalize(normal),1) I get this:

[spoiler]

[/spoiler]

Which is expected, since every face has the same normal. Now, this part is what confuses me.

float diffuseLightIntensity = max(0,dot(normalize(normal),lightDir));

I take the dot product of the normal and the direction of the light. The normal remains the same for every part of the face, while the lightDir is different for every fragment of the face. Taking the dot product should give me a different value for each fragment, since lightDir is different for each fragment. Am I correct? Either way, diffuseLightIntensity remains the same for every fragment of the face, which can be seen on the first image.

I’m probably just misunderstanding something. If I’m doing something really stupid that I should be able to fix myself, then sorry for wasting your time. But hopefully I shouldn’t make myself look like a fool. :stuck_out_tongue:

Any help is appreciated. :slight_smile:

You’re getting exactly what I expect you to get with those normals. You need to specify different normals per vertex so you can interpolate between them. If all vertices in a triangle have the same normal then “per-pixel” lighting is going to use that normal for all pixels --> same result as per vertex lighting.

The problem is the normal data you feed. The skull probably has 3 unique vertices for each triangle, and these vertices all have the same normal. I assume you loaded the model from file. If the format does not store vertex normals you need to calculate them yourself. One way to calculate the normal of a vertex is adding together the normals of all the triangles that share that vertex and then normalizing it (when loading the model, not in the shader).

The world need more shaders! Keep it up!

Problem is allways in the normals.

I get what you’re saying but I don’t believe that’s the problem. I think I managed to smooth the normals, but the problem is still there. Please verify that I’ve understood everything about the lighting calculations correctly.

Currently I take the dot product of the normal (which remains constant for every face) and the normalized distance vector from the pixel to the light source. Take a look at this image:

Every black block represents a pixel, the yellow circle the light source, every red line a normal, and every green line the vector from the light source to the pixel.

The distance vector from the light source to the pixel will be different for every pixel since it’s calculated in the fragment shader. The normal will of course remain the same for every pixel. As seen in the image, the angle between the two vectors varies so taking the dot product of the two should give different values for different pixels, right? Why isnt this the case? ???

Are you using a direction light or a point light?

Point light.

It’s the normals.

Your shader code works perfectly fine. Try to put a light very very close to the skeleton and you´ll see that the color really varies per pixel.

Are you using an index buffer to draw the skeleton or not? I think you´re creating unique vertices for each triangle, am I correct? And you set the normal of all those vertices to the same value. Currently, the light direction vector is per pixel, but the vertex normals aren´t even per vertex, but per triange duplicated to all 3 vertices. “Smooth” interpolation between 3 equal values is still the same value.

[quote]Your shader code works perfectly fine. Try to put a light very very close to the skeleton and you´ll see that the color really varies per pixel.
[/quote]
You’re right, it does work the way it should! Oh wow, now I feel really stupid. I guess the light was just too far away to make the lighting vary pixel by pixel.

[quote]Are you using an index buffer to draw the skeleton or not? I think you´re creating unique vertices for each triangle, am I correct?
[/quote]
You are correct, but I smoothed the normals the way you suggested earlier, by adding the normal of all the vertices that has the same position and then normalizing them. However, there must be something wrong with the code that does that. Anyway, thanks for you help. You’ve been really helpful :slight_smile:

No problem. I went through the same problems when I did the same thing as you. It’s really hard to actually see if the lighting is accurate… :wink: