Зеркальное отражение неверно

Я пытаюсь сделать небольшой 3D-движок для своей степени магистра (и для своего мастерства). У меня проблема с зеркальным отражением. (Извините за ссылку на иллюстративное изображение, но мне пока не хватает репутации). Все исходники доступны на моем GitHub: DWRenderer

Изображение проблемы

Здесь мы наблюдаем за объектом, но камера находится впереди, а также источник света. Как мы видим, за объектом есть отражение.

Для описания реальных параметров все расчеты производятся в мировом пространстве (нормально... с такой проблемой, сомневаюсь). Я поместил камеру в положение vec3(0, 0, 3) для теста, и свет — это просто точка в vec3(1.2, 1, 2), представленная кубом. Я использую Qt 5.4 и OpenGL 4.1 под Ubuntu с драйверами Nvidia.

Вот мой вершинный шейдер:

#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;
}

И мой фрагментный шейдер:

#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);
}

И код в игровом цикле (paintGL со временем с интервалом 16мс для реалтайма) для инициализации юниформ-переменной (Положение камеры для шейдера фиксированное, могу повернуть свой куб для проверки бага. Положение света находится в "initializeGL" и также исправлено):

// 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);

Я пытался выполнить вычисления в пространстве просмотра, но это не сработало. Я пытался изменить/нормализовать/использовать max() вместо clip(), но через несколько часов проблема возникла. У меня нет никаких идей.


person Zethzer    schedule 03.02.2016    source источник
comment
где определен ваш объект, где он находится в центре?   -  person Guiroux    schedule 03.02.2016
comment
Он определяется в функции initializeGL (вершины и нормали) и использует VAO и VBO. Он по центру origin(0,0,0). По исходникам на github это файл renderer.cpp   -  person Zethzer    schedule 03.02.2016
comment
я не понимаю фразу there's a reflection behind the object, что вы называете позади? поскольку свет и зритель находятся на одной стороне объекта (обе положительные позиции по сравнению с нулевой позицией для куба по оси z), как вы можете видеть behind ?   -  person Guiroux    schedule 03.02.2016
comment
Я могу видеть сзади, перемещая камеру. Но для шейдера я выполняю расчеты с фиксированной камерой в (0,0,3). Таким образом, я могу проверить рендеринг позади объекта.   -  person Zethzer    schedule 03.02.2016
comment
так что изображение, которое вы связали, не показывает реальную проблему?   -  person Guiroux    schedule 03.02.2016
comment
да. Конечно. На картинке у вас вид на заднюю часть куба. Но камера спереди и свет тоже.   -  person Zethzer    schedule 03.02.2016
comment
Большое спасибо за ваше время! Видимо, из-за какой-то глупости. Отражение вычисляется по положению обзора и положению источника света (и т. д.), но в OpenGL кажется, что столкновения нет. Таким образом, отражение вычисляет переднюю грань и внутреннюю переднюю грань (заднюю грань), и если мы двигаемся, чтобы увидеть обратную сторону, мы видим это вычисление. (изнутри). Если я перемещаю камеру нормально, все вычисления кажутся правильными.   -  person Zethzer    schedule 03.02.2016
comment
именно то, что я пытался объяснить в своем ответе, и я нарисовал красивую схему :-(, но тот факт, что вы знаете, почему он дает ошибки с фиксированными значениями тестирования, возможно, не объясняет всего, кстати, схема для тех, кто не понял :-P i.stack.imgur.com/yDZgK.png   -  person Guiroux    schedule 03.02.2016
comment
Извините за потраченное время на схему :( Но, как я уже сказал, спасибо за ваше время :) И я больше никогда не использую фиксированное значение для позиций :p только цвета :p   -  person Zethzer    schedule 03.02.2016
comment
но я думал, что вы используете фиксированную позицию, потому что у вас сначала были ошибки, чтобы попытаться отладить ее, что угодно, все работает нормально? чудесный   -  person Guiroux    schedule 03.02.2016
comment
Нет точно. Я установил камеру просто для удовольствия и посмотреть, что мы получим. И то, что я думал, что он был жуком, он не был ^^'   -  person Zethzer    schedule 04.02.2016


Ответы (1)


У вас есть рассеянный и зеркальный свет только в том случае, если lightDir (направление от фрагмента к свету) находится в направлении norm (нормальный вектор фрагмента). Если они направлены против, то можно обойтись без рассеянного и зеркального света. Другими словами, если рассеянного света нет (потому что diff это 0.0), то и зеркального света тоже нет. Адаптируйте свой код следующим образом:

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);
}
person Rabbid76    schedule 04.02.2016
comment
Спасибо за ответ, но я не понимаю, почему - person Zethzer; 04.02.2016
comment
@Zethzer Если lightDir направлен на norm, фрагмент подсвечивается на задней стороне, поэтому вы не можете видеть свет на лицевой стороне. - person Rabbid76; 04.02.2016