Specular highlights depend on camera distance

2.3k views Asked by At

I just tried implementing specular highlights. The issue is that when moving far away from the surface, the highlight becomes stronger and stronger and the edge of the highlight becomes very harsh. When moving too near to the surface, the highlight completely disappears.

This is the related part of my fragment shader. All computations are in view space. I use a directional sun light.

// samplers
vec3  normal    = texture2D(normals,   coord).xyz;
vec3  position  = texture2D(positions, coord).xyz;
float shininess = texture2D(speculars, coord).x;

// normalize directional light source
vec3 source;
if(directional) source = position + normalize(light);
else source = light;

// reflection
float specular = 0;
vec3 lookat = vec3(0, 0, 1);
float reflection = max(0, dot(reflect(position, normal), lookat));
int power = 5;
specular = shininess * pow(reflection, power);

// ...

// output
image = color * attenuation * intensity * (fraction + specular);

This is a screenshot of my lighting buffer. You can see that the foremost barrel has no specular highlight at all while the ones far away shine much too strong. The barrel in the middle is lighted as desired.

far highlights are too strong and near highlights disappear

What am I doing wrong?

2

There are 2 answers

6
Pedro Boechat On BEST ANSWER

You're calculating the reflection vector from the object position instead of using the inverted light direction (pointing from object to light source).

It's like using the V instead of the L in this diagram:

Also, I think shininess should be the exponent of your expression not something that multiplies linearly the specular contribution.

2
a.lasram On

I think variables naming is confusing you. From what I'm reading (assuming you're in camera space and without handedness knowledge)

vec3 lookat = vec3(0, 0, 1);
float reflection = max(0, dot(reflect(position, normal), lookat));

lookat is a directional light and position is the actual lookat.

Make sure normal(it's probably already normalized) and position(the lookat) are normalized.

A less confusing code would be:

vec3 light_direction = vec3(0, 0, 1);
vec3 lookat = normalize(position-vec3(0,0,0));
float reflection = max(0, dot(reflect(light_direction, normal), -lookat));

Without normalizing position, reflection will be biased. The bias would be strong when position is far from the camera vec3(0,0,0)

Note how lookat is not a constant; it changes for each and every position. lookat = vec3(0,0,1) is looking toward a single position in view space.