4

Currently, I am implementing good old Phong shading. Overall it looks quite right but there is a pattern in the normals emerging, that I cannot explain.

Without a closer look, the Stanford Bunny looks quite correct, I think. enter image description here

But on the ears for example there is a strange pattern: enter image description here

In this picture I visualized the normals and boosted the saturation to make the problem more visible. enter image description here

This is my vertex shader:

#version 330 core

layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec3 vNormal;

out vec4 fWorldPosition;
smooth out vec3 fWorldNormalSmooth;

...    

void main() {
    fWorldNormalSmooth = normalize(NormalMatrix*vNormal);
    fWorldPosition = WorldMatrix*vPosition;
    gl_Position = ProjectionMatrix*ViewMatrix*WorldMatrix*vPosition;
}

This is my fragment shader:

#version 330 core

smooth in vec3 fWorldNormalSmooth;
in vec4 fWorldPosition;

out vec4 color;

...    

vec4 shadePointLight(Material material, PointLight pointLight, vec3 worldPosition, vec3 worldNormal) {
    vec3 cameraPosition = wdiv(inverse(ViewMatrix)*vec4(0, 0, 0, 1));
    vec3 cameraDirection = normalize(cameraPosition - worldPosition);
    vec3 lightDirection = normalize(pointLight.position - worldPosition);
    vec3 reflectionDirection = reflect(-lightDirection, worldNormal);

    vec4 i_amb  = material.ambientReflection*pointLight.ambientColor;
    vec4 i_diff = max(0, dot(worldNormal, lightDirection))*material.diffuseReflection*pointLight.diffuseColor;
    vec4 i_spec = pow(max(0, dot(reflectionDirection, cameraDirection)), material.shininess)*material.specularReflection*pointLight.specularColor;

    float distance = length(pointLight.position - worldPosition);
    float d  = 1.0 / (pointLight.falloff.constant + pointLight.falloff.linear*distance + pointLight.falloff.quadratic*distance*distance);

    return i_amb + d*(i_diff + i_spec);
}

void main() {
    ...

    color = shadePointLight(material, pointLight, wdiv(fWorldPosition), normalize(fWorldNormalSmooth));
}

Can someone explain this behaviour?

Henkk
  • 609
  • 6
  • 18

1 Answers1

6

When interpolating linearly between two vectors of identical length, as happens between vertex and fragment stage, the length of the resulting vector will be shorter in between. The mathenatically correct way to interpolate between two normals is to perform spherical linear interpolation (SLERP), however for small changes in angle you can get away with simply normalize the interpolated normal vector in the fragment shader (that is because of the small angle approximation sin(x) ≈ x for small x). EDIT: For larger angles through a proper SLERP interpolation is required.

datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • I believe that the normalizaiton is already done in the fragment shader at this line: `color = shadePointLight(material, pointLight, wdiv(fWorldPosition), normalize(fWorldNormalSmooth));` – Sunreef Jun 28 '16 at 10:03
  • 1
    @Sunreef: Ah, actually it is. Didn't see that (clipped at the right border of my screen). – datenwolf Jun 28 '16 at 10:10