Bump Mapping в OpenGL и GLSL

Я пытаюсь реализовать рельефное отображение на кубе с использованием OpenGL и GLSL. Однако, когда я поворачиваю свой куб вокруг, появляются только левый квадрат и правый квадрат (то есть, в отрицательном направлении x и положительном x), остальные четыре грани куба (верх, низ, перед, зад) черные. Вот пример:

введите описание изображения здесь

Мой класс сетки состоит из indexList и vertexList (класс Vertex содержит x, y, z и т. Д.). Я использую эту простую модель cube.ply, которая содержит s, t текстурные координаты. Заимствуя из этого примера, я вычисляю касательные векторы следующим образом:

void Mesh::computeTangents() {

        for (size_t i = 0; i < m_indexList.size(); i += 3) {

            Vertex v1 = m_vertexList[m_indexList[i]];
            Vertex v2 = m_vertexList[m_indexList[i+1]];
            Vertex v3 = m_vertexList[m_indexList[i+2]];

            glm::vec3 pos1 = glm::vec3(v1.getX(), v1.getY(), v1.getZ());
            glm::vec3 pos2 = glm::vec3(v2.getX(), v2.getY(), v2.getZ());
            glm::vec3 pos3 = glm::vec3(v3.getX(), v3.getY(), v3.getZ());
            glm::vec2 tex1 = glm::vec2(v1.getS(), v1.getT());
            glm::vec2 tex2 = glm::vec2(v2.getS(), v2.getT());
            glm::vec2 tex3 = glm::vec2(v3.getS(), v3.getT());

            glm::vec3 edge1 = glm::normalize(pos2 - pos1);
            glm::vec3 edge2 = glm::normalize(pos3 - pos1);

            glm::vec2 texEdge1 = glm::normalize(tex2 - tex1);
            glm::vec2 texEdge2 = glm::normalize(tex3 - tex1);

            float det = (texEdge1.x * texEdge2.y) - (texEdge1.y * texEdge2.x);

            glm::vec3 tangent;

            if(fabsf(det) < 1e-6f) {
                tangent.x = 1.0;
                tangent.y = 0.0;
                tangent.z = 0.0;
            }
            else {
                det = 1.0 / det;

                tangent.x = (texEdge2.y * edge1.x - texEdge1.y * edge2.x) * det;
                tangent.y = (texEdge2.y * edge1.y - texEdge1.y * edge2.y) * det;
                tangent.z = (texEdge2.y * edge1.z - texEdge1.y * edge2.z) * det;

                glm::normalize(tangent);
            }

            m_vertexList[m_indexList[i]].setTanX(tangent.x);
            m_vertexList[m_indexList[i]].setTanY(tangent.y);
            m_vertexList[m_indexList[i]].setTanZ(tangent.z);
            m_vertexList[m_indexList[i+1]].setTanX(tangent.x);
            m_vertexList[m_indexList[i+1]].setTanY(tangent.y);
            m_vertexList[m_indexList[i+1]].setTanZ(tangent.z);
            m_vertexList[m_indexList[i+2]].setTanX(tangent.x);
            m_vertexList[m_indexList[i+2]].setTanY(tangent.y);
            m_vertexList[m_indexList[i+2]].setTanZ(tangent.z);
        }
    }

Если я выведу значения касательного вектора для каждого треугольника, я получу эти значения:
1, 0, 0
1, 0, 0
0, 0, -1
0, 0, -1
0, 0, 1
0, 0, 1
-1, 0, 0
-1, 0, 0,
1, 0, 0
1, 0, 0
1, 0, 0
1, 0, 0

Если они верны, то проблема, скорее всего, в шейдере. Мой шейдер выглядит следующим образом (в основном из книги):

верт:

attribute vec4 vertexPosition;
attribute vec3 vertexNormal;
attribute vec2 vertexTexture;
attribute vec3 vertexTangent;

varying vec2 texCoord;
varying vec3 viewDirection;
varying vec3 lightDirection;

uniform vec3 diffuseColor;
uniform float shininess;
uniform vec4 lightPosition;

uniform mat4 modelViewMatrix;
uniform mat4 normalMatrix;
uniform mat4 MVP; // modelview projection

void main() {
    vec4 eyePosition = modelViewMatrix * vertexPosition;
    vec3 N = normalize(vec3(normalMatrix * vec4(vertexNormal, 1.0)));
    vec3 T = normalize(vec3(normalMatrix * vec4(vertexTangent, 1.0)));
    vec3 B = normalize(cross(N, T));

    vec3 v;
    v.x = dot(lightPosition.xyz, T);
    v.y = dot(lightPosition.xyz, B);
    v.z = dot(lightPosition.xyz, N);
    lightDirection = normalize(v);

    v.x = dot(eyePosition.xyz, T);
    v.y = dot(eyePosition.xyz, B);
    v.z = dot(eyePosition.xyz, N);
    viewDirection = normalize(v);

    texCoord = vertexTexture;
    gl_Position = MVP * vertexPosition;
}

Frag:

varying vec2 texCoord;
varying vec3 viewDirection;
varying vec3 lightDirection;

uniform vec3 diffuseColor;
uniform float shininess;

void main() {

    float bumpDensity = 16.0;
    float bumpSize = 0.15;

    vec2 c = bumpDensity * texCoord;
    vec2 p = fract(c) - vec2(0.5);

    float d, f;
    d = dot(p, p);
    f = 1.0 / sqrt(d + 1.0);

    if (d >= bumpSize) {
        p = vec2(0.0);
        f = 1.0;
    }

    vec3 normalDelta = vec3(p.x, p.y, 1.0) * f;
    vec3 litColor = diffuseColor * max(dot(normalDelta, lightDirection), 0.0);
    vec3 reflectDir = reflect(lightDirection, normalDelta);
    float spec = max(dot(viewDirection, reflectDir), 0.0);
    spec *= shininess;
    litColor = min(litColor + spec, vec3(1.0));
    gl_FragColor = vec4(litColor, 1.0);
 } 

Изменить: Изменен фон, чтобы более четко видеть черные лица. Кроме того, я неправильно отметил, какие лица на самом деле появлялись раньше.

Редактировать 2: Я обнаружил, что значение max(точка (normalDelta, lightDirection), 0,0) в фрагментном шейдере возвращает 0 для этих граней. Тем не менее, я до сих пор не знаю, почему.

Редактировать 3: Ну, проблема оказалась в том, что я передавал неправильный индекс касательных векторов в моем классе Vertex. То есть у меня было 10 в этой строке вместо 9:

glVertexAttribPointer(v3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(sizeof( float ) * 9));  

1 ответ

Решение

Итак, проблема в том, что любая грань с нормалью на плоскости xz не рендерится должным образом, то есть те, которые рендерится правильно, это те, которые имеют нормали вдоль оси y (верхняя и нижняя грани).

И ваш список касательных:

 1, 0,  0
 1, 0,  0
 0, 0, -1
 0, 0, -1
 0, 0,  1
 0, 0,  1
-1, 0,  0
-1, 0,  0
 1, 0,  0
 1, 0,  0
 1, 0,  0
 1, 0,  0

Все либо вдоль осей X или Z. Так что я предполагаю, что это может произойти из-за того, что ваши нормали и касательные указывают в одинаковых (или противоположных) направлениях, и в этом случае эта линия в вашем вершинном шейдере:

vec3 B = normalize(cross(N, T));

приведет к vec3(0.0, 0.0, 0.0), который не может быть нормализован.

Моя рекомендация состоит в том, чтобы попытаться вручную задать грани x и z вдоль оси y, чтобы узнать, имеет ли это значение. Если это помогает, то вы знаете, что проблема заключается в вычислении касательных.

Если это не помогает, вы можете устранить неполадки путем вывода значений из фрагментного шейдера в виде экранных цветов. Попробуйте отобразить viewDirection, lightDirection, normalDelta, refleDir и все остальное, что вы можете придумать, чтобы увидеть, какая переменная вызывает затемнение.

Бонус: переключить цвет glClear на что-то кроме черного? Так что это не похоже на лицо, плавающее в пространстве.

Другие вопросы по тегам