Artifacts with Blinn-Phong illumination

Hi. I’ve been experimenting a bit with implementing Blinn-Phong illumination in GLSL. I have mostly gotten in to work, but I’m getting a few annoying artifacts in the specular highlights on polygons facing away from light sources. I’ve attached an image showing the problem:

My shader looks like this:

[Vertex]
uniform mat4 u_projection_view_world, u_view_world;
varying vec3 v_surface_normal, v_eye_to_surface;

mat3 rotation_part(mat4 m)
{
	return mat3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2]);
}

void main()
{
	gl_Position = u_projection_view_world * gl_Vertex;
	v_eye_to_surface = u_view_world * gl_Vertex;
	v_surface_normal = rotation_part(u_view_world) * gl_Normal;
}

[Fragment]
uniform mat4 u_view;

uniform struct material_properties
{
	vec4 ambient;
	vec4 diffuse;
	vec4 specular;
	float shininess;
} u_material;

#if DIRECTIONAL_LIGHT_COUNT > 0
uniform struct directional_light_properties
{
	vec4 ambient;
	vec4 diffuse;
	vec4 specular;
	vec3 direction;
} u_directional_lights[DIRECTIONAL_LIGHT_COUNT];
#endif

#if POINT_LIGHT_COUNT > 0
uniform struct point_light_properties
{
	vec4 ambient;
	vec4 diffuse;
	vec4 specular;
	vec3 position;
	float constant_attenuation;
	float linear_attenuation;
	float quadratic_attenuation;
} u_point_lights[POINT_LIGHT_COUNT];
#endif

varying vec3 v_surface_normal, v_eye_to_surface;

mat3 rotation_part(mat4 m)
{
	return mat3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2]);
}

vec4 to_vec4(vec3 v)
{
	return vec4(v[0], v[1], v[2], 1.0);
}

void main()
{
	vec3 normal = normalize(v_surface_normal);
	vec3 eye_dir = normalize(-v_eye_to_surface);
	vec4 color = vec4(0.0, 0.0, 0.0, 1.0);
	
	#if DIRECTIONAL_LIGHT_COUNT > 0
	for(int i = 0; i < DIRECTIONAL_LIGHT_COUNT; ++i)
	{
		vec3 light_dir = normalize(rotation_part(u_view) * u_directional_lights[i].direction);
		float n_dot_l = clamp(dot(normal, light_dir), 0.0, 1.0);
		vec3 half_vector = normalize(light_dir + eye_dir);
		float n_dot_hv = clamp(dot(normal, half_vector), 0.0, 1.0);

		color += u_directional_lights[i].ambient * u_material.ambient;
		color += n_dot_l * u_directional_lights[i].diffuse * u_material.diffuse;
		color += u_material.specular * u_directional_lights[i].specular * pow(n_dot_hv, u_material.shininess);
	}
	#endif
	
	#if POINT_LIGHT_COUNT > 0
	for(int i = 0; i < POINT_LIGHT_COUNT; ++i)
	{
		vec3 surface_to_light = u_view * to_vec4(u_point_lights[i].position) - v_eye_to_surface;
		vec3 light_dir = normalize(surface_to_light);
		float light_distance = length(surface_to_light);
		float attenuation = 1.0 / (u_point_lights[i].constant_attenuation + u_point_lights[i].linear_attenuation * light_distance
				+ u_point_lights[i].quadratic_attenuation * light_distance * light_distance);
		float n_dot_l = clamp(dot(normal, light_dir), 0.0, 1.0);
		vec3 half_vector = normalize(light_dir + eye_dir);
		float n_dot_hv = clamp(dot(normal, half_vector), 0.0, 1.0);
		
		color += attenuation * u_point_lights[i].ambient * u_material.ambient;
		color += attenuation * n_dot_l * u_point_lights[i].diffuse * u_material.diffuse;
		color += attenuation * u_material.specular * u_point_lights[i].specular * pow(n_dot_hv, u_material.shininess);
	}
	#endif
	
	gl_FragColor = color;
}

Now, the scene has a point light in the middle of the “fortress”, and the artifact shows up along the line between the eye position and the light position. This has lead me to believe the way I’m calculating the half-vector is to blame, since at that point the eye and light direction vectors would cancel each other out.

Am I right in that assumption? If so, how should I be doing this? I’ve seen a few examples dropping the specular contribution if n_dot_l is negative, but that leads to a very visible cutoff-point where you see the specular highlight disappear. Are there any other solutions?

Any help would be much appreciated. :slight_smile:

dropping the specular contribution if n_dot_l is negative
Do it, but not with a boolean test : instead use the smoothstep function to cut this gradually. Adjust to your liking.
For example, in 3dsmax you have a “soften highlight” slider.

Thanks! That worked beautifully. :slight_smile:

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.