Problems with Modern OpenGL lighting

Hello. I am creating a 3D world where I send matrices to a shader to create this. I am now trying to light up my world but it is causing me trouble. This is something I’ve been struggling for weeks on.

Here’s my fragment shader code:
#version 120

varying vec4 color;
varying vec4 normal;
varying vec4 vertex;

uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform mat4 normalMatrix;

void main() {
mat4 modelView = viewMatrix * modelMatrix;
vec3 vertexPos = (modelView * vertex).xyz;
vec3 surfaceNormal = normalize((normalMatrix*normal).xyz);
vec3 lightDirection = normalize(gl_LightSource[0].position.xyz - vertexPos);
float diffuseLightIntensity = max(0, dot(surfaceNormal, lightDirection));
gl_FragColor.rgb = diffuseLightIntensity * color.rgb;
gl_FragColor += gl_LightModel.ambient;
vec3 reflectionDirection = normalize(reflect(-lightDirection, surfaceNormal));
float specular = max(0.0, dot(surfaceNormal, reflectionDirection));

if (diffuseLightIntensity != 0) {
    float fspecular = pow(specular, gl_FrontMaterial.shininess);
    gl_FragColor += fspecular;
}

}

Here’s my vertex shader code:
#version 120

varying vec4 color;
varying vec4 normal;
varying vec4 vertex;

uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform mat4 normalMatrix;

void main() {
color = gl_FrontMaterial.diffuse;
normal = vec4(gl_Normal, 1.0);
vertex = gl_Vertex;
gl_Position = projectionMatrix * viewMatrix * modelMatrix * gl_Vertex;
}

I have my own maths classes that send the code to the shader and they work fine. Am I missing something with the code? I think I’m drawing the vertices correctly, by using a VBO. Cheers.

Here’s what the lighting looks like: http://i.imgur.com/BSEZ9qc.png

there shouldn’t be any matrices in the fragment shader. Calculate the position, normal and lightdir in the vertex shader. You only need to normalize the normal in the fragment shader.

your fragment shader should look something like this(from my code base):


    vec3 N = normalize(normal);

    vec3 color, dcolor;
    color = vec3(0.);
    dcolor = diffuse;

    float NdotL = dot(N, light_pos);

    if(NdotL > 0.0){
        float HdotN = dot(N, halfvector);
        vec3 scolor = specular;
        #ifdef SPECULAR_MAPPING
            scolor *= texture(specular_sampler, texcoord).rgb;
        #endif
        color += scolor * pow(max(HdotN, 0.), mat_spec_exp);
        color += dcolor * NdotL;
    }
    color += ambient * dcolor;

where “halfvector” is the vector with half the angle between the eye and the light vector. Calculating the lighting in eye-space instead of world-space(what you tried to do), makes it much easier to calculate this halfvector. Just place this in the vertex shader:


  vec3 normal = view_matrix * normal_matrix * in_normal;
  vec3 halfvector = normalize(normal + vec3(0,0,1))

@Danny02

Hi, thanks for your reply. I’ve tried mess around with it and something is still wrong. Here is the code:

Vertex Shader:

#version 120

varying vec4 color;
varying vec3 vertex;
varying vec3 normal;

uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform mat4 normalMatrix;

void main() {
color = gl_Color;
vertex = vec3(viewMatrix * modelMatrix * gl_Vertex);
normal = vec3(normalize(normalMatrix * gl_Vertex));
gl_Position = projectionMatrix * viewMatrix * modelMatrix * gl_Vertex;
}

Fragment Shader:
#version 120

varying vec4 color;
varying vec3 vertex;
varying vec3 normal;

vec3 lightSource = vec3(0, 10, 0);
vec4 ambientTerm = vec4(0.1, 0.1, 0.1, 1);
vec4 diffuse = vec4(0.5, 0.5, 0.5, 1);
vec4 specular = vec4(0.1, 0.1, 0.1, 1);
float shininess = 0.1;

void main() {
vec3 light = normalize(lightSource - vertex);
vec3 eyeCoords = normalize(-vertex);
vec3 reflection = normalize(-reflect(light, normal));

vec4 diffuseTerm = diffuse * max(dot(normal, light), 0.0);
diffuseTerm = clamp(diffuseTerm, 0.0, 1.0);

vec4 specularTerm = specular * pow(max(dot(reflection, eyeCoords),0.0), 0.3 * shininess);
specularTerm = clamp(specularTerm, 0.0, 1.0);

gl_FragColor = color + ambientTerm + diffuseTerm+ specularTerm;

}

Have you tried to calculate it like this?

glFragColor = color * (ambientTerm + diffuseTerm + specularTerm);

@Longarmx

Yes, the colour gave a darker image, but the light keeps moving around the world with the camera.


#version 120

varying vec4 color;
varying vec3 vertex;
varying vec3 normal;

uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform mat4 normalMatrix;

void main() {
    color = gl_Color;
    vertex = vec3(viewMatrix * modelMatrix * gl_Vertex);
-    normal = vec3(normalize(normalMatrix * gl_Vertex));
+    normal = vec3(viewMatrix * normalMatrix * normal);
    gl_Position = projectionMatrix * viewMatrix * modelMatrix * gl_Vertex;
}

Fragment Shader:
#version 120

varying vec4 color;
varying vec3 vertex;
varying vec3 normal;

vec3 lightSource = vec3(0, 10, 0);
vec4 ambientTerm = vec4(0.1, 0.1, 0.1, 1);
vec4 diffuse = vec4(0.5, 0.5, 0.5, 1);
vec4 specular = vec4(0.1, 0.1, 0.1, 1);
float shininess = 0.1;

void main() {
+   vec3 N = normalize(normal);
   vec3 light =  normalize(lightSource - vertex);
   vec3 eyeCoords = normalize(-vertex);
-   vec3 reflection = normalize(-reflect(light, normal));
+   vec3 reflection = reflect(light, N);
   
   vec4 diffuseTerm = diffuse * max(dot(N, light), 0.0);
-   diffuseTerm = clamp(diffuseTerm, 0.0, 1.0);
   
   vec4 specularTerm = specular * pow(max(dot(reflection, eyeCoords),0.0), 0.3 * shininess);
   specularTerm = clamp(specularTerm, 0.0, 1.0);
   
-    gl_FragColor = color + ambientTerm + diffuseTerm+ specularTerm;
+    gl_FragColor = color * (ambientTerm + diffuseTerm) + specularTerm;
}

Your shininess should probably also be bumped up a lot. 5+ is a good start. Under 1 looks weird as hell.

Okay, the lighting kinda works but I can adjust it later. But whenever the camera rotates or moves the light seems to move. Any reason why?

Here is the updated shaders:

vs
#version 120

varying vec4 color;
varying vec4 vertex;
varying vec4 normal;

uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform mat4 normalMatrix;

void main() {
color = gl_Color;

mat4 modelView = viewMatrix * modelMatrix;
mat4 modelViewProjection = projectionMatrix * viewMatrix * modelMatrix;

vertex = modelView * gl_Vertex;
normal = normalMatrix * vec4(gl_Normal, 1.0);
gl_Position = modelViewProjection * gl_Vertex;	

}

fs
#version 120

varying vec4 color;
varying vec4 vertex;
varying vec4 normal;

vec4 lightPos = vec4(5, 0, 5, 1.0);
vec4 ambientTerm = vec4(0.25, 0.25, 0.25, 1.0);
vec4 diffuse = vec4(0.5, 0.5, 0.5, 1.0);
vec4 specular = vec4(0.5, 0.5, 0.5, 1.0);
float shininess = 25;

void main() {
vec4 N = normalize(normal);
vec4 light = normalize(lightPos - vertex);
vec4 eyeCoords = normalize(-vertex);
vec4 reflection = reflect(light, N);

vec4 diffuseTerm = diffuse * max(dot(N, light), 0.0);
//diffuseTerm = clamp(diffuseTerm, 0.0, 1.0);

vec4 specularTerm = specular * pow(max(dot(reflection, -eyeCoords), 0.0), shininess);
specularTerm = clamp(specularTerm, 0.0, 1.0);

gl_FragColor = color * (ambientTerm + diffuseTerm) + specularTerm;
}

The light direction is constant in view space and not in world-space, because your lighting calculation happens in view space. Declare it as a uniform and transform it each frame into view-space on the CPU.


Vec3 lightDir = ...;
Matrix4 view = ...;

Vec3 vsLightDir = view.mult(lightDir);
shader.updateLightDir(vsLightDir);

renderScene();

Thanks everyone! It works now! http://i.imgur.com/hoTKoxP.png
If anyone is having the same issue just message me and I’ll give you the code.

To get rid of the noticeable banding issues, you can add a random value (between 0 and 1/256th) to each fragment. Due to the rounding when writing to the framebuffer, you should get an ultra smooth gradient, not like noise at all - think of it as dithering.

Update: (I plugged in some arbitrary primes to make the dithering patterns less obvious)

Zoomed in: (top: banding, bottom: dithering, showing my poorly chosen constants)

Looking good Nouht! Still up to 0x10c? ;D

Cheers for that @Riven. Tried it, it works great!

They way most people did it was to have an uniform array with data for multiple lights. Such an array will have a constant size, which may be choosen either by performance or memory concerns.

If you have more lights in your scene then you can render at once, you can do two things. Either choose only the most important light sources for each object, or render each object in multiple passes.

Lets say you can only render 5 lights at the same time, but have many 13 lights in total:

  1. somehow order the lights for each object(e.g. 1/distance² * intensity) and only select the top 5.
  2. render all lights in 3 passes which are blended together. Only apply the ambient term once.

@danny02

Thanks for that. So every time a make another pass at the render the last light in the array always seems to override the other lights, so it only renders the last one