3
votes

I'm trying to make a little 3D engine for my master degree (and for my skill). I have a problem on the specular reflection. (I'm sorry for the link for the illustration image but I haven't enough reputation yet). All sources is available on my GitHub : DWRenderer

Image of the problem

Right here, we're behing the object but the camera is on the front also the light. As we can see, there's a reflection behind the object.

For describe the actual parameters, all the computations are made in world space (normally... with that problem, I've a doubt). I put the camera on position vec3(0, 0, 3) for the test and the light is just a point at vec3(1.2, 1, 2) represent by a cube. I'm using Qt 5.4 and OpenGL 4.1 under Ubuntu with Nvidia drivers.

Here's my vertex shader :

#version 410 core

layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;

out vec3 Normal;
out vec3 FragPos;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform mat3 normalMatrix;

void main()
{
    gl_Position = projection * view * model * vec4(position, 1.0f);
    FragPos = vec3(model * vec4(position, 1.0f));
    Normal = normalMatrix * normal;
}

And my fragment shader:

#version 410 core

out vec4 color;

in vec3 Normal;
in vec3 FragPos;

struct Material {
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float shininess;
};

struct Light {
    vec3 position;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

uniform Material material;
uniform Light light;

uniform vec3 viewPos;

void main()
{
    // Vectors
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(light.position - FragPos);
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);

    // Ambient
    vec3 ambient = material.ambient * light.ambient;

    // Diffuse
    float diff = clamp(dot(lightDir, norm), 0.0, 1.0);
    vec3 diffuse = diff * material.diffuse * light.diffuse;

    // Specular - The bug seems only here
    float spec = pow(clamp(dot(viewDir, reflectDir), 0.0, 1.0), material.shininess);
    vec3 specular = spec * material.specular * light.specular;

    vec3 result = (diffuse + specular + ambient);
    color = vec4(result, 1.0f);

    // For test vectors
    //color = vec4(specular, 1.0f);
}

And the code in the gaming loop (paintGL with a time with an interval of 16ms for real-time) for initialize the uniform variable (The position of the camera for the shader is fixed, i can turn around my cube for check the bug. The position of the light is in "initializeGL" and fixed too) :

// Draw cube
    m_cubeShader->useShaderProgram();
    GLint lightPosLoc = glGetUniformLocation(m_cubeShader->getId(), "light.position");
    GLint viewPosLoc = glGetUniformLocation(m_cubeShader->getId(), "viewPos");
    GLint matAmbientLoc  = glGetUniformLocation(m_cubeShader->getId(), "material.ambient");
    GLint matDiffuseLoc  = glGetUniformLocation(m_cubeShader->getId(), "material.diffuse");
    GLint matSpecularLoc = glGetUniformLocation(m_cubeShader->getId(), "material.specular");
    GLint matShineLoc    = glGetUniformLocation(m_cubeShader->getId(), "material.shininess");
    GLint lightAmbientLoc  = glGetUniformLocation(m_cubeShader->getId(), "light.ambient");
    GLint lightDiffuseLoc  = glGetUniformLocation(m_cubeShader->getId(), "light.diffuse");
    GLint lightSpecularLoc = glGetUniformLocation(m_cubeShader->getId(), "light.specular");
    glUniform3f(lightAmbientLoc,  0.2f, 0.2f, 0.2f);
    glUniform3f(lightDiffuseLoc,  0.5f, 0.5f, 0.5f);
    glUniform3f(lightSpecularLoc, 1.0f, 1.0f, 1.0f);
    glUniform3f(matAmbientLoc,  1.0f, 0.5f, 0.31f);
    glUniform3f(matDiffuseLoc,  1.0f, 0.5f, 0.31f);
    glUniform3f(matSpecularLoc, 0.5f, 0.5f, 0.5f);
    glUniform1f(matShineLoc,    32.0f);
    glUniform3f(viewPosLoc, 0.0f, 0.0f, 3.0f); // For testing a bug - Unresolved
    //glUniform3f(viewPosLoc, m_camera->getPosition().x, m_camera->getPosition().y, m_camera->getPosition().z);
    glUniform3f(lightPosLoc, m_lightPos.x, m_lightPos.y, m_lightPos.z);

    glm::mat4 model;
    glm::mat4 view;
    glm::mat4 projection;
    glm::mat3 normalMatrix;
    normalMatrix = glm::mat3(glm::transpose(glm::inverse(model)));
    view = m_camera->getViewMatrix();
    projection = glm::perspective(glm::radians(m_camera->getFov()), (GLfloat)m_screenWidth / (GLfloat)m_screenHeight, 0.1f, 100.0f);
    GLint normalMatrixLoc = glGetUniformLocation(m_cubeShader->getId(), "normalMatrix");
    GLint modelLoc = glGetUniformLocation(m_cubeShader->getId(), "model");
    GLint viewLoc = glGetUniformLocation(m_cubeShader->getId(), "view");
    GLint projectionLoc = glGetUniformLocation(m_cubeShader->getId(), "projection");
    glUniformMatrix3fv(normalMatrixLoc, 1, GL_FALSE, glm::value_ptr(normalMatrix));
    glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
    glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
    glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));

    glBindVertexArray(m_cubeVAO);
    glDrawArrays(GL_TRIANGLES, 0, 36);
    glBindVertexArray(0);

    // Draw light
    m_lightShader->useShaderProgram();

    model = glm::mat4();
    model = glm::translate(model, m_lightPos);
    model = glm::scale(model, glm::vec3(0.2f));
    modelLoc = glGetUniformLocation(m_lightShader->getId(), "model");
    viewLoc = glGetUniformLocation(m_lightShader->getId(), "view");
    projectionLoc = glGetUniformLocation(m_lightShader->getId(), "projection");
    glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
    glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
    glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));

    glBindVertexArray(m_lightVAO);
    glDrawArrays(GL_TRIANGLES, 0, 36);
    glBindVertexArray(0);

I've tried to do the computations on view space but it doesn't work. I've tried to modify/normalize/use max() instead of clamp() but after a few hours on the problem. I'm out of any ideas.

1
where is your object defined, where is it centered ?Guiroux
He's define in initializeGL function (vertices and normal) and using a VAO and VBO. He's centered on origin (0,0,0).On the sources on github, it's the file renderer.cppZethzer
i don't get the phrase there's a reflection behind the object, what do you call behind ? since the light and viewer are the same side of the object (both positive positions compared to null position for cube on z axis), how can you see behind ?Guiroux
I can see "behind" by moving the camera. But for the shader, i compute the calculations with a fixed camera at (0,0,3). Like that, i can verify the rendering behind the object.Zethzer
exactly what i was trying to explain in my answer and i drawn a nice scheme :-( , but the fact that you know why it bugs with fixed testing values maybe doesn't explain everything ,btw the scheme for anybody who would have not understood :-P i.stack.imgur.com/yDZgK.pngGuiroux

1 Answers

1
votes

You only have diffuse and specular light if the lightDir (direction from fragment to light) is in the direction of norm ( normal vector of fragment ). If they are directed against you can do without diffuse and specular light. In other words, if ther isn't any diffuse light (because diff is 0.0), there isn't any specular light too. Adapt your code like this:

void main()
{
    // Vectors
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(light.position - FragPos);
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);

    // Ambient
    vec3 ambient = material.ambient * light.ambient;

    vec3 result = ambient;
    float dotNvLd = dot( norm, lightDir );
    if ( dotNvLd > 0.0 ) // test if normal vector not directed against vector to light position
    {
        // Diffuse
        float diff = min( dotNvLd, 1.0 );
        vec3 diffuse = diff * material.diffuse * light.diffuse;

        // Specular - The bug seems only here
        float spec = pow(clamp(dot(viewDir, reflectDir), 0.0, 1.0),   material.shininess);
        vec3 specular = spec * material.specular * light.specular;

        result = (diffuse + specular + ambient);
    }

    color = vec4(result, 1.0f);

    // For test vectors
    //color = vec4(specular, 1.0f);
}