PDA

View Full Version : Artifacts with Blinn-Phong illumination



wien
05-30-2007, 01:49 PM
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:

http://www.wien-systems.no/forum_attachments/blinn_phong_artifact_001.jpg

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. :)

ZbuffeR
05-30-2007, 02:41 PM
dropping the specular contribution if n_dot_l is negativeDo 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.

wien
05-30-2007, 03:10 PM
Thanks! That worked beautifully. :)

http://www.wien-systems.no/forum_attachments/blinn_phong_artifact_fixed.jpg