-1

With a cube defined as in the following code, you see that normals are often negative in one axis. (even if we calcultate them)

OpenGL manages it with its fixed pipeline, correct me if I'm wrong, but with programmable pipeline, it causes artifacts like black faces. (My previous stackoverflow question provides code.)

I managed to run my code with an operation on my normals (normal = (0.5 + 0.5 * normal); ), but even if the result looks ok, I wonder if my normals are still valid? (And is this operation the best?)

I mean, from a shader point of view, can I still use them to shade or brighten my models? How do you usually do?

The mentionned normals:

const GLfloat cube_vertices[] = {
  1, 1, 1,  -1, 1, 1,  -1,-1, 1,      // v0-v1-v2 (front)
  -1,-1, 1,   1,-1, 1,   1, 1, 1,      // v2-v3-v0
  1, 1, 1,   1,-1, 1,   1,-1,-1,      // v0-v3-v4 (right)
  1,-1,-1,   1, 1,-1,   1, 1, 1,      // v4-v5-v0
  1, 1, 1,   1, 1,-1,  -1, 1,-1,      // v0-v5-v6 (top)
  -1, 1,-1,  -1, 1, 1,   1, 1, 1,      // v6-v1-v0
  -1, 1, 1,  -1, 1,-1,  -1,-1,-1,      // v1-v6-v7 (left)
  -1,-1,-1,  -1,-1, 1,  -1, 1, 1,      // v7-v2-v1
  -1,-1,-1,   1,-1,-1,   1,-1, 1,      // v7-v4-v3 (bottom)
  1,-1, 1,  -1,-1, 1,  -1,-1,-1,      // v3-v2-v7
  1,-1,-1,  -1,-1,-1,  -1, 1,-1,      // v4-v7-v6 (back)
  -1, 1,-1,   1, 1,-1,   1,-1,-1 };    // v6-v5-v4

const GLfloat cube_normalsI[] = {
  0, 0, 1,   0, 0, 1,   0, 0, 1,      // v0-v1-v2 (front)
  0, 0, 1,   0, 0, 1,   0, 0, 1,      // v2-v3-v0
  1, 0, 0,   1, 0, 0,   1, 0, 0,      // v0-v3-v4 (right)
  1, 0, 0,   1, 0, 0,   1, 0, 0,      // v4-v5-v0
  0, 1, 0,   0, 1, 0,   0, 1, 0,      // v0-v5-v6 (top)
  0, 1, 0,   0, 1, 0,   0, 1, 0,      // v6-v1-v0
  -1, 0, 0,  -1, 0, 0,  -1, 0, 0,      // v1-v6-v7 (left)
  -1, 0, 0,  -1, 0, 0,  -1, 0, 0,      // v7-v2-v1
  0,-1, 0,   0,-1, 0,   0,-1, 0,      // v7-v4-v3 (bottom)
  0,-1, 0,   0,-1, 0,   0,-1, 0,      // v3-v2-v7
  0, 0,-1,   0, 0,-1,   0, 0,-1,      // v4-v7-v6 (back)
  0, 0,-1,   0, 0,-1,   0, 0,-1 };    // v6-v5-v4
genpfault
  • 51,148
  • 11
  • 85
  • 139
Leon
  • 554
  • 4
  • 18
  • What do You mean by 'black faces'? Face usually turns black or dark when its normal vector is facing in a direction that forms an angle bigger than 90 deg with a light direction vector. Check whether the signs of the normals are set properly. – Dori Jun 13 '18 at 09:50
  • 1
    The whole question does only make sense if you want to visualize the normals. If you use the normals for what they are intended (light calculation, etc.), then it's perfectly fine when they contain negative values. – BDL Jun 13 '18 at 11:18

1 Answers1

3

No, this makes no sense at all. Either you need to update your question or you got it all wrong.

Normal may face any direction and normals are often negative in one axis is completely natural. Why wouldn't they be? From what you are describing you seem to be working with lighting. A part of lighting uses normal to see what is the angle between light source and surface. The idea here is that when you turn the normal a light ray effectively lightens a larger part of surface which reduces density of reflected light. With basic math you can see that the correlation is cos(angle) so parallel vectors will produce highest brightness. Since we are using vectors we are better of replacing cos with dot product.

So at some point you have

float factor = dot(normalize(normal), normalize(lightSource-surfacePoint))

Let's have 2 examples here:

normal = (0, 1, 0)
lightSource = (0, 1, 0)
surfacePoint = (0, 0, 0)
dot((0, 1, 0), (0, 1, 0)) = 0+1+0 = 1

and turn it around:

normal = (-1, 0, 0)
lightSource = (-3, 1, 0)
surfacePoint = (0, 1, 0)
dot((-1, 0, 0), normalize(-3, 0, 0)) = dot((-1, 0, 0), (1, 0, 0)) = 1+0+0 = 1

so even if positions are completely changed and normals negative we will get the same result for same angles (in these cases the vectors being perpendicular).

The only question here is what to do when dot product is negative. That happens when normal faces away from the light. In your case you have a cube and all normals point outwards. What if you needed to be inside a cube and still have lighting? You will get

normal = (0, 1, 0)
lightSource = (0, 0, 0)
surfacePoint = (0, 1, 0)
dot((0, 1, 0), (0, -1, 0)) = 0-1+0 = -1

Because of such cases you need to either clam the values or use absolute values. Clamping will produce interior of cube to be black (not lighted) while absolute value will light those as well:

fragmentColor += lightColor*dotFactor // Do nothing and your light will darken the area
fragmentColor += lightColor*abs(dotFactor) // Use absolute value to lighten even if facing away
fragmentColor += lightColor*max(0.0, dotFactor) // Clamp minimum so there are no negative values.

But none of these have nothing to do with normals facing any direction in absolute coordinate system. It just has to do with relative positions between normal, pixel location and light source.

Matic Oblak
  • 16,318
  • 3
  • 24
  • 43
  • Thank you for your answer which seems to be the good one. I often read in tutorials that we could check normals by displaying them as colors, to see a blue/purple image, you all see what I mean. According to your answer, I 'd need light in order to do this simple visualisation on a cube? – Leon Jun 13 '18 at 14:50
  • @GUNNM Not at all. Since normals are usually normalized (length of 1) all their values will be within [-1, 1] so you can show them as colors by either clamping or using absolute values. In case of cubes (without applying matrix) you would see red, green and blue faces. You could also for instance use (x+1)/2 on each component to get them into correct range. But I am not sure the result would be interesting visually. It would probably not make sense unless you have some insane ability to distinguish colors. But even with clamped or absolute; can you imagine what (0.5, 0.6, 5.5) looks like? – Matic Oblak Jun 13 '18 at 14:57