Why are my specular highlights showing so much at the edges of the polygon?

I have a simple application that draws a sphere with one directional light. I create a sphere starting at an octahedron and dividing each triangle into 4 smaller triangles.

With just diffused lighting, the sphere looks very smooth. However, when I add specular highlights, the edges of the triangles appear quite strongly. Here are some examples:

Diffusion only:

Sphere with only diffuse lighting

Diffuse and specular:

Sphere with diffuse and specular

I believe the normals are interpolated correctly. Looking at the normals, I get this:

Sphere normals

In fact, if I switch to flat shading, where the normals are for polygon instead of per-vertex, I get this:

enter image description here

In my vertex shader, I multiply the model normals by the transformed inverse of the form:

#version 330 core

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

out vec3 fNormal;
out vec2 fTexCoord;

uniform mat4 transInvModelView;
uniform mat4 ModelViewMatrix;
uniform mat4 ProjectionMatrix;

void main()
{
    fNormal = vec3(transInvModelView * vec4(vNormal, 0.0));
    fTexCoord = vTexCoord;
    gl_Position = ProjectionMatrix * ModelViewMatrix * vPosition;
}

      

and in the fragment shader, I calculate the specular highlights as follows:

#version 330 core

in vec3 fNormal;
in vec2 fTexCoord;

out vec4 color;

uniform sampler2D tex;
uniform vec4 lightColor;        // RGB, assumes multiplied by light intensity
uniform vec3 lightDirection;    // normalized, assumes directional light, lambertian lighting
uniform float specularIntensity;
uniform float specularShininess;
uniform vec3 halfVector;        // Halfway between eye and light
uniform vec4 objectColor;

void main()
{
    vec4    texColor    = objectColor;

    float   specular    = max(dot(halfVector, fNormal), 0.0);
    float   diffuse     = max(dot(lightDirection, fNormal), 0.0);

    if (diffuse == 0.0)
    {
        specular = 0.0;
    }
    else
    {
        specular = pow(specular, specularShininess) * specularIntensity;
    }

    color = texColor * diffuse * lightColor + min(specular * lightColor, vec4(1.0));
}

      

I was a little confused about how to calculate halfVector

. I do this on a cpu and pass it as a uniform. It is calculated as follows:

vec3    lightDirection(1.0, 1.0, 1.0);
lightDirection = normalize(lightDirection);
vec3    eyeDirection(0.0, 0.0, 1.0);
eyeDirection = normalize(eyeDirection);
vec3    halfVector  = lightDirection + eyeDirection;
halfVector = normalize(halfVector);
glUniform3fv(halfVectorLoc, 1, &halfVector [ 0 ]);

      

Is this the correct wording for halfVector

? Or should it be done in shaders?

+3


source to share


1 answer


Interpolating normals to a face can (and almost always will) cause the normal to shrink. This is why the highlight is darker in the center of the face and brighter at the corners and edges. If you do, just change the normal mode in the fragment shader:

fNormal = normalize(fNormal);

      



Btw, you cannot pre-compose the half vector as it depends on the view (which is the whole point of specular lighting). In the current scenario, the backlight will not change when you just move the camera (keeping the direction).

One way to do this in a shader is to pass in an additional shape for the position of the eyes and then compute the direction of the view as eyePosition - vertexPosition

. Then proceed as on the CPU.

+8


source







All Articles