McLovin
McLovin

Reputation: 3417

OpenGL diffuse lighting calculates wrong

I have simple scene that involves a rotating cube and a directional (diffuse) light source, but I don't seem to get the lighting math right.

I load the vertices and normals from the obj file from this tutorial:

Some of my application code:

glm::mat4 modelview = ...

glm::mat4 projection = glm::perspective(60.0f, 16.0f/9.0f, 0.1f, 100.0f);

glm::mat4 MVP = projection * modelview;
glm::mat3 NormalMatrix = glm::transpose(glm::inverse(glm::mat3(modelview)));

glUniformMatrix4fv(MVP_location, 1, GL_FALSE, &MVP[0][0]);
glUniformMatrix3fv(NormalMatrix_location, 1, GL_FALSE, &NormalMatrix[0][0]);
glDrawArrays(GL_TRIANGLES, 0, 36);

Vertex shader:

#version 430

in vec3 position;
in vec3 in_normal;
uniform mat4 MVP;
uniform mat3 NormalMatrix;

out vec3 Normal;
out vec4 Color;

void main()
{
    gl_Position = MVP * vec4(position, 1.0);

    Color = vec4(clamp(position, 0.0, 1.0), 1.0); //based on vertex position
    Normal = normalize(NormalMatrix * in_normal);
}

Fragment shader:

#version 430

const vec3 Ambient = vec3(0.15, 0.15, 0.15);
const vec3 LightDirection = normalize(vec3(1.0, 0.0, 1.0));
const vec3 LightColor = vec3(1.0, 1.0, 1.0);

const float Shininess = 20.0;
const float Strength = 5.0;

in vec3 Normal;
in vec4 Color;

out vec4 FragColor;

void main()
{
    FragColor = Color;

    float diffuse = max(0.0, dot(Normal, LightDirection));

    vec3 scatteredLight = Ambient + LightColor * diffuse;

    vec3 rgb = min(FragColor.rgb * scatteredLight, vec3(1.0));

    FragColor = vec4(rgb, Color.a);
}

This is what I get: my fail

Any idea what am I doing wrong?

Upvotes: 0

Views: 1438

Answers (1)

Joey Dewd
Joey Dewd

Reputation: 1824

Let me elaborate a bit more on dari's comment.

Your normal vector is multiplied with a normal matrix derived from the view*model matrix (called modelview in your application). Multiplying vertices with the model matrix transforms them from local-space to world-space and multiplying vertices in world-space with a view matrix transforms them to view-space coordinates. In your case, you transformed the normal vector with the model and view matrix; this means that your normal vectors are in view-space (their direction, as seen from the viewer's perspective: the camera).

Now, you specified the light's direction vector LightDirection in world-space (its direction is how you want it to be in the global world right? No need to transform it in any way with a model matrix).

When calculating diffuse/specular lighting, you want your direction/position vectors to all reside in the same coordinate space, otherwise you're doing calculations between different spaces which generates weird results. Since your LightDirection is in world-space and your Normal in view-space you get weird results.

What you thus need to do to solve your issue is either:

  • Transform LightDirection to view-space by multiplying it with the view matrix (not modelview since that contains both the model and view matrix).
  • or calculate your normal matrix from just the model matrix (not modelview) so your normal vector will end up in world-space just like LightDirection currently is.

The important thing is that both vectors are in the same space (can be either world or view, whatever you prefer).

Note that this applies to when you want to give the impression the camera is moving while the objects are stationary (allowing you to move to unlit sides of your objects); see Reto Koradi's comment below.

Upvotes: 2

Related Questions