Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Artifacts from fragment shading in OpenGL

Trying to learn 3D programming, I am currently implementing a class which batch-renders cubes with OpenGL by pushing them to the GPU each frame. I tried to implement some crude lighting by 1) Sending each face normal to the GPU as part of the vertex attributes 2) Checking the direction of said normal and darken each fragment arbitrarily, like so:

Fragment Shader

#version 330

in vec2 ex_TexCoord;
in vec3 ex_Normal;
out vec4 out_Color;
uniform sampler2D textureSampler;

void main(void) {
  float lightAmplifier = 1.0;

  if (ex_Normal.z == 1.0) {
    lightAmplifier = .8;
  } else if (ex_Normal.x == 1.0) {
    lightAmplifier = .65;
  } else if (ex_Normal.x == -1.0) {
    lightAmplifier = .50;
  }

  out_Color = vec4(texture2D(textureSampler, ex_TexCoord).rgb * lightAmplifier, 1.0);
}

Resulting in this:

Screenshot

While not being very good at GLSL, my intuition says this is not correct behaviour. But a number google searches later, I'm still not sure what I'm doing wrong.

like image 274
KaffeLeffe Avatar asked Mar 17 '18 13:03

KaffeLeffe


2 Answers

This is a floating point precision issue. Note the vertex coordinates are interpolated when for each fragment. It may always lead to issues, when comparing floating point numbers on equality.

On possible solution would be to use an epsilon (e.g. 0.01) and to change the comparison to < -0.99 and > 0.99:

if (ex_Normal.z > 0.99) {
    lightAmplifier = .8;
} else if (ex_Normal.x > 0.99) {
    lightAmplifier = .65;
} else if (ex_Normal.x < -0.99) {
    lightAmplifier = .50;
} 


Another possibility would be to use the flat interpolation qualifier, which causes the value not to be interpolated:

Vertex shader:

flat out vec3 ex_Normal;

Fragment shader:

flat in vec3 ex_Normal;
like image 174
Rabbid76 Avatar answered Sep 28 '22 00:09

Rabbid76


Rabbid76 is (probably) right about the erroneous appearance being a result of floating point accuracy/precision.

What I'd like to add is that setting a constant value based on testing an interpolated value is rarely a good idea (some algorithms do depend on it though, e.g. shadow mapping). Changing the test from ==1 to >=0.99 just puts the error at 0.99 intead of at 1.0. It is more likely you want to interpolate the value when it enters a certain range, e.g. by using mix.

like image 27
Andreas Avatar answered Sep 28 '22 02:09

Andreas