Very strange shading effect

Hi all. I’ve been attempting to teach myself glsl shading using vertex and fragment shaders. I’ve made some decent progress using sphere code I found online along with the shading model described in a textbook. However, I am getting a very strange effects with my finished product. I’ve attached a picture.

I believe my error is with the sphere normals, but I’m not sure what I’m doing wrong or how I can fix it. Here is the code that generates my sphere.


   for ( i = 0; i < numParallels + 1; i++ )
   {
      for ( j = 0; j < numSlices + 1; j++ )
      {
         int vertex = ( i * (numSlices + 1) + j ) * 3;

         if ( vertices )
         {
            (*vertices)[vertex + 0] = radius * sinf ( angleStep * (float)i ) *
                                               sinf ( angleStep * (float)j );
            (*vertices)[vertex + 1] = radius * cosf ( angleStep * (float)i );
            (*vertices)[vertex + 2] = radius * sinf ( angleStep * (float)i ) *
                                               cosf ( angleStep * (float)j );
         }

         if ( normals )
         {
            (*normals)[vertex + 0] = (*vertices)[vertex + 0];
            (*normals)[vertex + 1] = (*vertices)[vertex + 1];
            (*normals)[vertex + 2] = (*vertices)[vertex + 2];
         }


I then load up the normals and vertex information in some buffers.

Here are my shaders - I apologize for the random unused variables. I’ve been trying to figure out why the heck I get these alternating ambient-color triangles.


#version 400

layout(location=0) in vec3 in_Position;
layout(location=1) in vec4 in_Color;
layout(location=2) in vec3 in_Normals;


out vec4 ex_Color;

out vec3 position;
out  vec3 fN;
out  vec3 fE;
out  vec3 fL;
out float specularExponent;
uniform mat4 ModelMatrix;
uniform mat4 ViewMatrix;
uniform mat4 ProjectionMatrix;
uniform vec4 light_position;
void main(void)
{

	fN = in_Normals;
	fE = in_Position;
	fL = light_position.xyz;
	if(light_position.w != 0.0)
	{
		fL = light_position.xyz - in_Position.xyz;
	}

	gl_Position = (ProjectionMatrix * ViewMatrix * ModelMatrix) * vec4(in_Position.x,in_Position.y,in_Position.z, 1);
	position = gl_Position.xyz; 
	ex_Color = vec4(.5,.3,.5,1);
	specularExponent = 100.0;
}

#version 400

in vec4 ex_Color;
in vec3 position;

in float specularExponent;
in  vec3 fN;
in  vec3 fL;
in  vec3 fE;
out vec4 fColor;



void main()
{
	vec4 light_color = vec4(1.0,1.0,1.0,1.0);
	vec4 ambient_light = vec4(.5,.5,.5,1.0);
	vec4 SpecularColor = vec4(1.0, 1.0, 1.0, 1.0);
    vec3 N = normalize(fN);
    vec3 E = normalize(fE);
    vec3 L = normalize(fL);
	

	vec3 H = normalize(L+E);
	vec4 amb = ex_Color * ambient_light;
	vec4 diff = max(dot(L,N), 0.0) * ex_Color * light_color;
	vec4 spec = pow( max (dot(N,H), 0.0), specularExponent) *  SpecularColor * light_color  ;
	if(dot(L,N) < 0.0){
		spec = vec4(0,0,0,1);
	}
	fColor = amb + diff + spec;
	//fColor = vec4(1,1,1,1);
}

I appreciate the help!

Joe

I have tried your shaders (I have modified them a little bit) on a simple tessellated plane and it work. So maybe there is a problem with the normal of the sphere. Some vertices may have wrong normals. My advice is to try first your shaders on a simple quad (2 triangles) . Then on a tessellated plane and move on to more complex geometry.

Also, I think the way you compute the specular component of your lighting equation is wrong.
I have changed to this:


....
 const vec3 R = reflect(-L,N); //reflect light vector on the surface
....
vec4 spec = pow( max ( dot(-E,R), 0.0), specularExponent) * SpecularColor * sun.color  ;

But to have the “right” result, I have to modify also your vertex shader. Because with your vertex shader, when you move your camera, the light move also. Maybe thats not what you want.

Here is the shaders I have modified.Maybe there still bugs but as I wrote previously, it work on a simple plane

Vertex shader:


#version 420

layout(location=0) in vec4 VertexPosition;
layout(location=1) in vec4 VertexColor;
layout(location=2) in vec4 VertexNormal;


struct Light
{
vec4 position ;
 vec4 color ;
};


out vec4 ex_Color;
out vec3 position;
out  vec3 fN;
out  vec3 fE;
out  vec3 fL;
out float specularExponent;
//uniform mat4 ModelMatrix;
//uniform mat4 ViewMatrix;
//uniform mat4 ProjectionMatrix;
//uniform vec4 light_position;


uniform mat4 MVPMatrix;
uniform mat4 MVMatrix;
uniform mat4 NMatrix; //Normal matrix

uniform Light sun;

void main(void)
{

	fN = (NMatrix * VertexNormal).xyz;
	fE = (MVMatrix*VertexPosition).xyz;
	fL = sun.position.xyz - fE;
	if(sun.position.w != 0.0)
	{
		fL = (sun.position.xyz) - fE;
	}

	gl_Position = (MVPMatrix) * VertexPosition;
	//position = gl_Position.xyz; 
	ex_Color =  VertexColor;
	specularExponent = 256.0;
}


fragment shader


#version 420

in vec4 ex_Color;
in vec3 position;

in float specularExponent;
in  vec3 fN;
in  vec3 fL;
in  vec3 fE;
out vec4 fColor;

struct Light
{
vec4 position ;
 vec4 color ;
};


uniform Light sun;

void main()
{
	//vec4 light_color = vec4(1.0,1.0,1.0,1.0);
	vec4 ambient_light = vec4(.0,.0,.0,1.0);
	vec4 SpecularColor = vec4(0.0, 1.0, 0.0, 1.0);
    vec3 N = normalize(fN);
    vec3 E = normalize(fE);
    vec3 L = normalize(fL);
	
    const vec3 R = reflect(-L,N);
	//vec3 H = normalize(L+E);
	vec4 amb = ex_Color * ambient_light;
	vec4 diff = max(dot(L,N), 0.0) * ex_Color * sun.color;
	vec4 spec = pow( max (dot(-E,R), 0.0), specularExponent) *  SpecularColor * sun.color  ;
	if(dot(L,N) < 0.0){
		spec = vec4(0,0,0,1);
	}
	fColor = amb + diff + spec;
	//fColor = vec4(1,1,1,1);
}


Uniform setup code


 MVMatrix = glm::mat4(1.0f);
  
  MVMatrix = glm::rotate(MVMatrix,0.0f,glm::vec3(1.0f,0.0f,0.0f));
  MVMatrix = glm::rotate(MVMatrix,0.0f,glm::vec3(0.0f,0.0f,1.0f));
  MVMatrix = glm::rotate(MVMatrix,0.0f,glm::vec3(0.0f,1.0f,0.0f));

  const glm::mat4 viewMatrix = camera.getViewMatrix(); /*glm::lookAt(position,
				      center,
				      glm::vec3(0.0f,1.0f,0.0f));*/
  MVMatrix =   viewMatrix * MVMatrix;

  const glm::vec4 ecSunPosition = viewMatrix * sunPosition;

 
  MVPMatrix =  projectionMatrix * MVMatrix;
//NORMAL matrix transformation
  NMatrix = glm::transpose(glm::inverse(MVMatrix));  

  program.uniformMatrix4fv(MVPMatrixLoc,1,GL_FALSE,&MVPMatrix[0][0]);
  program.uniformMatrix4fv(MVMatrixLoc,1,GL_FALSE,&MVMatrix[0][0]);
  program.uniformMatrix4fv(NMatrixLoc,1,GL_FALSE,&NMatrix[0][0]);
  program.uniform4fv(SunPositionLoc,1,&ecSunPosition[0]);
  program.uniform4fv(SunColorLoc,1,&sunColor[0]);

You know, I’d forgotten that I had copied that code for changing the light source location when I was testing! No wonder the light source was doing funky things when I would do transformations on the view matrix. Thank you for catching that.

From what you’re saying, it sounds like I need to recalculate the normals, as they are incorrect. The only other thing I can think of is that I am misaligning the data in the buffers; here is the code I used:


    glEnableVertexAttribArray(0);//vertices
    glEnableVertexAttribArray(1);//colors
    glEnableVertexAttribArray(2);//normals
    glGenBuffers(4, &BufferIds[1]);//vertices, colors, normals, and indices

    glBindBuffer(GL_ARRAY_BUFFER, BufferIds[1]);//bind buffer for vertices
    glBufferData(GL_ARRAY_BUFFER, vsize * sizeof(GLfloat), vertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0); //vertices are in layout position 0, 3 floats, all packed

    glBindBuffer(GL_ARRAY_BUFFER, BufferIds[2]);//bind buffer for colors
    glBufferData(GL_ARRAY_BUFFER, csize * sizeof(GLfloat), colors, GL_STATIC_DRAW);
    glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);//colors in layout 1, 4 floats, packed

    glBindBuffer(GL_ARRAY_BUFFER, BufferIds[3]);
    glBufferData(GL_ARRAY_BUFFER, nsize * sizeof(GLfloat), normals, GL_STATIC_DRAW);
    glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0); //normals in layout 2, 3 floats, packed
    
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, BufferIds[4]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, isize * sizeof(GLuint), indices, GL_STATIC_DRAW);
    glBindVertexArray(0);

What is the size of BufferIds variable? Because in C/C++, array indice begin at 0. So when you declare GLuint BufferIds[4], valid indice are [0 to 3]. But I don’t think that this will remove your bug.Also the vertex shader the I have supplied need some modification to work properly


...
const vec4 fN4 = NMatrix * VertexNormal;
	fN = (fN4 / fN4.w).xyz;
	const vec4 fE4 = (MVMatrix*VertexPosition);
	fE = (fE4/fE4.w).xyz;
...

fL = (sun.position.xyz/sun.position.w) - fE;


In your sphere creation function try to print out the values of normal with small tessellation values (numParallels=2,numSlices=2 for example). Finally, what type of primitive you draw, triangles, triangle strip, quad or quad strip?. The primitive you draw influence the values you put in your element array buffer.

BufferIds is size 5, so I’m allocating four IDs starting from 1 and going up to 4. BufferIds[0] is where my VAO goes.

I’m drawing triangle strips with the indices/vertex data, here is the output printed out by a run with 3 sphere slices.

Vertices: x: 0.000000 y: 3.000000 z: 0.000000 normals: x: 0.000000 y: 1.000000 z: 0.000000
Vertices: x: 0.000000 y: 3.000000 z: -0.000000 normals: x: 0.000000 y: 1.000000 z: -0.000000
Vertices: x: -0.000000 y: 3.000000 z: -0.000000 normals: x: 0.000000 y: 1.000000 z: -0.000000
Vertices: x: 0.000000 y: 3.000000 z: 0.000000 normals: x:0.000000 y: 1.000000 z: 0.000000
Vertices: x: 0.000000 y: -1.500000 z: 2.598076 normals: x: 0.000000 y: -0.500000 z: 0.866025
Vertices: x: 2.250000 y: -1.500000 z: -1.299038 normals: x: 0.750000 y: -0.500000 z: -0.433013
Vertices: x: -2.250000 y: -1.500000 z: -1.299038 normals: x: -0.750000 y: -0.500000 z: -0.433013
Vertices: x: 0.000000 y: -1.500000 z: 2.598076 normals: x: 0.000000 y: -0.500000 z: 0.866025

The normals look good. The other problem I see is maybe the winding order of the triangle strip. Do you enable cull face? Depending on the state variable you set, triangles with clock wise winding will not be drawn. Verify the indices you put in the element array buffer. Then without lighting enabled, draw your sphere in wire frame mode to see if all triangles look good.

I tested the modified shaders on a curved surface and it work for me.

I do have cull face enabled.

The odd thing is this: when I disable cull face, I don’t get the alternating triangle effect, and diffuse and ambient light work fine, but specular doesn’t work properly.

When I enable cull face, specular works (as in the screenshot), but I get the alternating light/dark triangles. It is very confusing.

If I turn off shading completely and do a wireframe, I get a perfect sphere.

I am attaching a zip with my source, if you would like to review it. Thank you for the help!

The problem was resolved by changing to GL_TRIANGLES from GL_TRIANGLES_STRIP. The odd thing is that the sphere code I used specifically says to use triangle strips. Thank you for your help.