Revisiting Lighting in GLSL ...

Hello,

I know folks have helped me in the past on this and I truly believe I am almost there.

So, after much work I believe my directional light is working in the scene, sort of, but I still get weird artifacts and effects.

Positional lighting doesn’t seem to work at all.

Also, my entire scene is poorly lit and I am not sure what I am doing wrong to have this binary “on, off” effect with lighting.

Anyway, here is what I am talking about.

With positional lighting coming from a point in one direction, the entire scene is dark, as shown below:

When I enable just directional lighting per my shader code; I get the light coming from the right direction but it essentially comes in as an “on, off” thing. You’ll see that I highlighted the areas in the image below where essentially the object is getting light and then right in the next pixel over there is no light:

To show people that I am loading normals, I did a simple “finalColor = NormalColor” colorizer in my shader code.

Here is the resultant image of the scene at the same angle:

Also, per usual, here is my vertex shader:


#version 330 core
#extension GL_ARB_explicit_attrib_location : require

layout(location = 0) in vec3 vPosition;
layout(location = 1) in vec3 vNormal;
layout(location = 2) in vec2 vUV;

layout (std140) uniform Sunlight
{
  
  vec4 SunlightPosition;
  vec4 SunlightDiffuse;
  vec4 SunlightSpecular;
  vec4 SunlightDirection;
  float constantAttenuation, linearAttenuation, quadraticAttenuation;
  float spotCutoff, spotExponent;
  float	EnableLighting;
  float	EnableSun;
  float ExtraValue;

};

out vec4 worldSpacePosition;  // position of the vertex (and fragment) in world space
out vec3 vertexNormalDirection;  // surface normal vector in world space
out vec2 TextureCoordinates;
out vec3 NormalColor;

uniform mat4 MVP;
uniform mat4 ModelMatrix;
uniform mat4 ViewMatrix;
uniform mat4 ViewModelMatrix;
uniform mat4 InverseViewMatrix;
uniform mat3 NormalMatrix;
 
void main()
{

	gl_Position = MVP * vec4(vPosition, 1.0);
	TextureCoordinates = vUV;
	worldSpacePosition = ModelMatrix * vec4(vPosition, 1.0);
	vertexNormalDirection = normalize(NormalMatrix * vNormal);
	NormalColor = vNormal;
	
}

And here is my fragment shader:


#version 330
#extension GL_ARB_explicit_attrib_location : require

precision highp float;

uniform mat4 MVP;
uniform mat4 ModelMatrix;
uniform mat4 ViewMatrix;
uniform mat4 ViewModelMatrix;
uniform mat4 InverseViewMatrix;
uniform mat3 NormalMatrix;

//
// These values vary per Mesh
//

uniform vec4 	AmbientMeshColor;
uniform vec4 	EmissiveMeshColor;
uniform vec4 	DiffuseMeshColor;
uniform vec4 	SpecularMeshColor;
uniform vec4	SceneBrightnessColor;
uniform float 	MeshShininess;
uniform float	ObjectHasTextureFile;

//
// Sunlight Settings.
//

layout (std140) uniform Sunlight
{
  
  vec4 SunlightPosition;
  vec4 SunlightDiffuse;
  vec4 SunlightSpecular;
  vec4 SunlightDirection;
  float constantAttenuation, linearAttenuation, quadraticAttenuation;
  float spotCutoff, spotExponent;
  float	EnableLighting;
  float	EnableSun;
  float ExtraValue;

};

uniform vec4		SceneAmbient;

//
// Whether Materials are enabled at all.
//

uniform float		IfEnableTextures;

//
// If we are just simply drawing the skybox.
//

uniform float		DrawingSkyBox;

uniform float 		DrawNormals;

uniform float		EnableWireframe;

uniform vec4		WireframeColor;

uniform float		TextureCoordinateDebug;

uniform sampler2D MainTextureSampler; 

in vec4 worldSpacePosition;
in vec3 vertexNormalDirection;
in vec2 TextureCoordinates;
in vec3 NormalColor;

vec4 finalDiffuseColor;

out vec4 finalColor;

void DrawSkyBox() {

	finalColor = texture(MainTextureSampler, TextureCoordinates);

}

void DrawWireFrame() {

	finalColor = WireframeColor;

}

void main()

{
  
	if (DrawingSkyBox != 1.0) {
	
		if (DrawNormals == 1.0) {
		
			finalColor = vec4(NormalColor, 1.0);
		
		} else {
		
			vec3 normalDirection = normalize(vertexNormalDirection);
		
			vec3 viewDirection = normalize(vec3(InverseViewMatrix * vec4(0.0, 0.0, 0.0, 1.0) - worldSpacePosition));
			
			vec3 lightDirection;
			
			float attenuation;
			
			if (SunlightPosition.w == 0.0) // directional light?
			{
				
				attenuation = 1.0; // no attenuation
				lightDirection = normalize(vec3(SunlightPosition));
				
			} 
			else // point light or spotlight (or other kind of light) 
			{
			
				vec3 positionToLightSource = vec3(SunlightPosition - worldSpacePosition);
				float distance = length(positionToLightSource);
				lightDirection = normalize(positionToLightSource);
				attenuation = 1.0 / (constantAttenuation
							   + linearAttenuation * distance
							   + quadraticAttenuation * distance * distance);
	 
				if (spotCutoff <= 90.0) // spotlight?
				{
				
					float clampedCosine = max(0.0, dot(-lightDirection, vec3(SunlightDirection)));
					if (clampedCosine < cos(radians(spotCutoff))) // outside of spotlight cone?
					{
						attenuation = 0.0;
					}
					else
					{
					
					  attenuation = attenuation * pow(clampedCosine, spotExponent); 
					  
					}
				}
			}
			
			vec4 ambientLighting = SceneAmbient * AmbientMeshColor;
			
			vec3 diffuseReflection;
			
			if (ObjectHasTextureFile == 1.0) {
			
				diffuseReflection = attenuation * vec3(SunlightDiffuse) * vec3(texture(MainTextureSampler, TextureCoordinates)) * max(0.0, dot(normalDirection, normalDirection));

			} else {
			
				diffuseReflection = attenuation * vec3(SunlightDiffuse) * vec3(DiffuseMeshColor) * max(0.0, dot(normalDirection, normalDirection));
			
			}
			
			vec3 specularReflection;
			
			if (dot(normalDirection, lightDirection) < 0.0) // light source on the wrong side?
			{
				
				specularReflection = vec3(0.0, 0.0, 0.0); // no specular reflection
				
			}
			else // light source on the right side
			{
				
				specularReflection = attenuation * vec3(SunlightSpecular) * vec3(SpecularMeshColor) * pow(max(0.0, dot(reflect(-lightDirection, normalDirection), viewDirection)), MeshShininess);
				
			}
			
			finalColor = vec4(vec3(ambientLighting) + diffuseReflection + specularReflection, DiffuseMeshColor.a);
		
		}

	} else {
	
		DrawSkyBox();
	
	}

}

Thank you for your time.

Should dot(normalDirection, normalDirection) be dot(normalDirection, lightDirection)? The former is just the square of the magnitude of normalDirection, which will be 1.0 (as normalDirection is normalised).

At present, lightDirection is only used in calculating specularReflection.

Thank you for catching that! Unfortunately, it didn’t make much of a difference:

Here is the updated fragment shader code:


#version 330
#extension GL_ARB_explicit_attrib_location : require

precision highp float;

uniform mat4 MVP;
uniform mat4 ModelMatrix;
uniform mat4 ViewMatrix;
uniform mat4 ViewModelMatrix;
uniform mat4 InverseViewMatrix;
uniform mat3 NormalMatrix;

//
// These values vary per Mesh
//

uniform vec4 	AmbientMeshColor;
uniform vec4 	EmissiveMeshColor;
uniform vec4 	DiffuseMeshColor;
uniform vec4 	SpecularMeshColor;
uniform vec4	SceneBrightnessColor;
uniform float 	MeshShininess;
uniform float	ObjectHasTextureFile;

//
// Sunlight Settings.
//

layout (std140) uniform Sunlight
{
  
  vec4 SunlightPosition;
  vec4 SunlightDiffuse;
  vec4 SunlightSpecular;
  vec4 SunlightDirection;
  float constantAttenuation, linearAttenuation, quadraticAttenuation;
  float spotCutoff, spotExponent;
  float	EnableLighting;
  float	EnableSun;
  float ExtraValue;

};

uniform vec4		SceneAmbient;

//
// Whether Materials are enabled at all.
//

uniform float		IfEnableTextures;

//
// If we are just simply drawing the skybox.
//

uniform float		DrawingSkyBox;

uniform float 		DrawNormals;

uniform float		EnableWireframe;

uniform vec4		WireframeColor;

uniform float		TextureCoordinateDebug;

uniform sampler2D MainTextureSampler; 

in vec4 worldSpacePosition;
in vec3 vertexNormalDirection;
in vec2 TextureCoordinates;
in vec3 NormalColor;

vec4 finalDiffuseColor;

out vec4 finalColor;

void DrawSkyBox() {

	finalColor = texture(MainTextureSampler, TextureCoordinates);

}

void DrawWireFrame() {

	finalColor = WireframeColor;

}

void main()

{
  
	if (DrawingSkyBox != 1.0) {
	
		if (DrawNormals == 1.0) {
		
			finalColor = vec4(NormalColor, 1.0);
		
		} else {
		
			vec3 normalDirection = normalize(vertexNormalDirection);
		
			vec3 viewDirection = normalize(vec3(InverseViewMatrix * vec4(0.0, 0.0, 0.0, 1.0) - worldSpacePosition));
			
			vec3 lightDirection;
			
			float attenuation;
			
			if (SunlightPosition.w == 0.0) // directional light?
			{
				
				attenuation = 1.0; // no attenuation
				lightDirection = normalize(vec3(SunlightPosition));
				
			} 
			else // point light or spotlight (or other kind of light) 
			{
			
				vec3 positionToLightSource = vec3(SunlightPosition - worldSpacePosition);
				float distance = length(positionToLightSource);
				lightDirection = normalize(positionToLightSource);
				attenuation = 1.0 / (constantAttenuation
							   + linearAttenuation * distance
							   + quadraticAttenuation * distance * distance);
	 
				if (spotCutoff <= 90.0) // spotlight?
				{
				
					float clampedCosine = max(0.0, dot(-lightDirection, vec3(SunlightDirection)));
					if (clampedCosine < cos(radians(spotCutoff))) // outside of spotlight cone?
					{
						attenuation = 0.0;
					}
					else
					{
					
					  attenuation = attenuation * pow(clampedCosine, spotExponent); 
					  
					}
				}
			}
			
			vec4 ambientLighting = SceneAmbient * AmbientMeshColor;
			
			vec3 diffuseReflection;
			
			if (ObjectHasTextureFile == 1.0) {
			
				diffuseReflection = attenuation * vec3(SunlightDiffuse) * vec3(texture(MainTextureSampler, TextureCoordinates)) * max(0.0, dot(normalDirection, lightDirection));

			} else {
			
				diffuseReflection = attenuation * vec3(SunlightDiffuse) * vec3(DiffuseMeshColor) * max(0.0, dot(normalDirection, lightDirection));
			
			}
			
			vec3 specularReflection;
			
			if (dot(normalDirection, lightDirection) < 0.0) // light source on the wrong side?
			{
				
				specularReflection = vec3(0.0, 0.0, 0.0); // no specular reflection
				
			}
			else // light source on the right side
			{
				
				specularReflection = attenuation * vec3(SunlightSpecular) * vec3(SpecularMeshColor) * pow(max(0.0, dot(reflect(-lightDirection, normalDirection), viewDirection)), MeshShininess);
				
			}
			
			finalColor = vec4(vec3(ambientLighting) + diffuseReflection + specularReflection, DiffuseMeshColor.a);
		
		}

	} else {
	
		DrawSkyBox();
	
	}

}

And here are the update images based on the above code:

With directional light, I still get the on/off effect:

And here is the normal shown from the exact same angle:

Looking more closely, your normals look wrong.

E.g. the right-facing surfaces on the left-hand wall are bright green, as are the viewpoint-facing surfaces on the stairs. On the pillar in the foreground, right-facing surfaces are red (and no part shows any green or blue); but the right-hand wall has red on viewpoint-facing surfaces.

Try replacing NormalMatrix with ModelMatrix just to eliminate one possible source of error. Provided that ModelMatrix contains only rotations, translations and uniform scaling, there’s no need for a separate normal matrix.

Also, much of the information is missing because you’re writing signed values to an unsigned texture format, so negative values are being clamped to zero; consider writing out (NormalColor+vec3(1))/2 instead.

Apart from that, try writing out individual lighting components (diffuse, specular) to narrow down the problem.

By the way:


#version 330 core
#extension GL_ARB_explicit_attrib_location : require

Explicit attribute location is part of OpenGL 3.3 (and thus GLSL 3.30). So you don’t have to specify it twice; just the version will be sufficient.

You were correct! My normal Matrix was being created from the MVP instead of the ModelMatrix.

This was causing all kinds of problems as folks can imagine.

I still need a bit of assistance but things are working a bit more consistent now.

[QUOTE=GClements;1264319]Looking more closely, your normals look wrong.

E.g. the right-facing surfaces on the left-hand wall are bright green, as are the viewpoint-facing surfaces on the stairs. On the pillar in the foreground, right-facing surfaces are red (and no part shows any green or blue); but the right-hand wall has red on viewpoint-facing surfaces.

Try replacing NormalMatrix with ModelMatrix just to eliminate one possible source of error. Provided that ModelMatrix contains only rotations, translations and uniform scaling, there’s no need for a separate normal matrix.

Also, much of the information is missing because you’re writing signed values to an unsigned texture format, so negative values are being clamped to zero; consider writing out (NormalColor+vec3(1))/2 instead.

Apart from that, try writing out individual lighting components (diffuse, specular) to narrow down the problem.[/QUOTE]

Thank you; will remove.

[QUOTE=Alfonse Reinheart;1264323]By the way:


#version 330 core
#extension GL_ARB_explicit_attrib_location : require

Explicit attribute location is part of OpenGL 3.3 (and thus GLSL 3.30). So you don’t have to specify it twice; just the version will be sufficient.[/QUOTE]