I am newbie to opengl. I did the tutorial by learnopengl. I have a strange problem when I was trying to do the light effect (ambient, diffuse and specular). For the diffuse, the effect appears on an incorrect surface, and it seems like the light position is always local on the origin point even if I change its position (the right cube position and the light position as the image shows):

The light now on the top of the right cube and there is no diffuse effect on the top surface of the cube, however the image shows that there is a light on the original point

I also tried to add the specular in a fragment shader but it also only appears on the left surface of the cube (the green arrow, that surface should not be lightened because now the vector is parallel the surface, so theoretically that surface should be black). I have passed the light position and camera position to the fragment shader of the cube (also the light fragment shader):

        glm::vec3 lightPos(0.0f, 2.5f, 3.0f);
        cubeShader->setVec3("lightPos", lightPos); 
        cubeShader->setVec3("cameraPos", secondCamera->Position);

and the fragment shader is, as simply as the tutorial's example:

    out vec4 FragColor;
    in vec3 Normal;
    in vec3 FragPos;
    in vec3 lightPos;
    uniform vec3 cameraPos;
    uniform vec3 lightColor;
    uniform vec3 objectColor;
    void main()
        //ambient CONDITION
        float ambientStrength = 0.1;
        vec3 ambient = ambientStrength * lightColor;        
        //diffuse CONDITION 
        vec3 norm = normalize(Normal);
        vec3 lightDirection = normalize(lightPos - FragPos);
        float diff = max(dot(norm, lightDirection), 0.0);
        vec3 diffuse =  diff* lightColor;
        //specular CONDITON
        float specularStrength = 0.5;
        vec3 viewDirection = normalize(cameraPos- FragPos);
        vec3 reflectDirection = reflect(-1.0 * lightDirection, norm);  
        float spec = pow(max(dot(viewDirection, reflectDirection), 0.0), 32);
        vec3 specular = specularStrength * spec * lightColor;  
        vec3 Result = (ambient + diffuse + specular) * objectColor;
        FragColor = vec4(Result, 1.0f);

Even when I add the specular, the specular appears on the incorrect surface. I found this answer, and I thought the cause might be the transformation of the model from camera to world space by using inverse and transpose:

However I did this in my vertex shader:

    layout (location = 0) in vec3 aPos;
    layout (location = 1) in vec3 aNormal;
    out vec3 Normal;
    out vec3 FragPos; 
    uniform mat4 model;
    uniform mat4 view;
    uniform mat4 projection;
    uniform vec3 cPosition[10];
    void main()
    vec3 newPosition = cPosition[gl_InstanceID];
    gl_Position =  projection * view  * model * vec4(aPos + newPosition,1.0);
    FragPos = vec3(model * vec4(aPos +newPosition,1.0)); 
    Normal = mat3(transpose(inverse(model))) * aNormal;

So, in summary, the problem is that when the light position changes, the effect will not change because it is only controlled by the "ghost" light which is located on the origin point. Could you guys please tell me, why there is a light on the origin? Thanks!


1 Answer 1


I believe you may be experiencing what happens when you use quantities belonging to distinctly different spaces. I have a number of questions for you which will hopefully lead you to your answer.

First, why are you using the inverse transpose of your model matrix to transform your normal? assuming your model matrix is your ObjectSpace => WorldSpace transformation, and your normal should be in object space, simply multiplying your normal by your Model matrix should map your normal into World Space.

Second, what's the deal with newPosition? It's not clear to me what this is for, but you appear to be translating your ObjectSpace quantities, which means your pipeline from ObjectSpace => ModelSpace => ViewSpace => NDC coordinates is no longer valid, as your ObjectSpace vertex is no longer centered at the origin? gl_Position should be the MVP * SomeVertexPosition in Object space, which converts it to NDC.

You may want to get in the habit of including a prefix for your shader variables which could help you denote which space each variable is in. When you move on to a more complicated phong implementation, you will have to deal with tangent space as well. I'm not sure if it's a comon practice, but labeling my spaces and labeling my matrices in a XSpaceToXSpace fashion certainly helps me keep track of where each variable is supposed to go, as well as help me track down the bugs quicker. You just see the names don't match up, and voila your hour-long bug hunt is cut short. I'll give you an example from one of my vertex shaders

  // deal with position and normal in world space  
  WorldPosition = ModelMatrix * vec4(vPosition, 1);

  //convert TBN vectors to view space
  WorldNormal = normalize(ModelMatrix * vec4(vNormal, 0));
  WorldTangent = normalize(ModelMatrix * vec4(vTangent,0.f));
  WorldBitangent = normalize(ModelMatrix * vec4(vBitangent,0.f));

  mat4 WorldToTangent;
  WorldToTangent[0] = WorldTangent;
  WorldToTangent[1] = WorldBitangent;
  WorldToTangent[2] = WorldNormal;
  WorldToTangent[3] = vec4(0.f,0.f,0.f,1.f);

  //because we orthonormalized, TBN will now be our TangentToWorld (the transpose of an orthogonal matrix is equivalent to its inverse)
  if (Material.NormalMapTextureEnabled)
    TBN = mat4(transpose(mat3(WorldToTangent)), vec4(0,0,0,1));;
    TBN = mat4(1.f);

  //Use TBN to convert world-space light positions and directions into tangent space
  for (int i=0;i<LightCount;++i)

  ViewVector = Material.NormalMapTextureEnabled ? TBN * vec4(Camera.Position - WorldPosition.xyz, 0) :vec4(Camera.Position - WorldPosition.xyz, 0) ;
  CameraPosition = Material.NormalMapTextureEnabled ? TBN * vec4(Camera.Position, 1.f) : vec4(Camera.Position, 1.f); 


  gl_Position = ModelViewProjectionMatrix * vec4(vPosition, 1);
  • \$\begingroup\$ Hi Dear Jon, thanks for your answer. For your questions: first, the normal matrix keeps the normal vector when the objects were transformed like compress. However the normal matrix does not contain the translate effect of the normal vector, that means, if I mulit the normal matrix with the model and its normal vector, I should got the a new normal vector which belongs to transformed model. \$\endgroup\$
    – GuangWu
    Sep 15, 2019 at 18:48
  • \$\begingroup\$ The second question, about newPosition. I have simply set a array which contains 10 object location, which like newPosition[1.0,2.0,3.0, .........] then the vertax shader take this data to give different location of 10 object like my image shows and as you see in my vertax shader : gl_Position = projection * view * model * vec4(aPos + newPosition,1.0);. In total, my problem is, it seems like I can not pass the light position into fragment shader and the fragment shader seems set a value (0,0,0) as a default to my light position. \$\endgroup\$
    – GuangWu
    Sep 15, 2019 at 18:53

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .