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 на что-то кроме черного? Так что это не похоже на лицо, плавающее в пространстве.