Нормальное отображение: матрица TBN отличается в вершинном шейдере по сравнению с фрагментным шейдером

Я работаю над реализацией нормального отображения для учебника и в учебных целях, я хотел бы передать матрицу TBN фрагментному шейдеру (из вершинного шейдера), чтобы я мог преобразовать нормальные векторы в касательном пространстве в мировое пространство для освещения расчеты. Нормальное отображение применяется к 2D-плоскости с нормальным направлением в положительном направлении z.

Однако, когда я вычисляю матрицу TBN в вершинном шейдере плоской плоскости (так что все касательные / битовые касания одинаковы для всех вершин), отображаемые нормали полностью отключаются. Хотя, если я передаю касательные / битовые и нормальные векторы к фрагментному шейдеру и создаю там TBN, он работает просто отлично, как показано на рисунке ниже (с отображенными нормалями):

нормальное отображение неверно

Вот где это становится странным. Поскольку плоскость плоская, векторы T, B и N одинаковы для всех ее вершин, поэтому матрица TBN также должна быть одинаковой для каждого фрагмента (поскольку интерполяция фрагмента ничего не меняет). Матрица TBN в вершинном шейдере должен быть точно такой же, как матрица TBN в фрагментном шейдере, но визуальные выводы говорят об обратном.

Исходный код вершинного и фрагментного шейдеров приведен ниже:

Vertex:

#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 texCoords;
layout (location = 3) in vec3 tangent;
layout (location = 4) in vec3 bitangent;

out VS_OUT {
vec3 FragPos;
vec3 Normal;
vec2 TexCoords;
vec3 Tangent;
vec3 Bitangent;
mat3 TBN;
} vs_out;

uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;

void main()
{
gl_Position = projection * view * model * vec4(position, 1.0f);
vs_out.FragPos = vec3(model * vec4(position, 1.0));
vs_out.TexCoords = texCoords;

mat3 normalMatrix = transpose(inverse(mat3(model)));
vs_out.Normal = normalize(normalMatrix * normal);

vec3 T = normalize(normalMatrix * tangent);
vec3 B = normalize(normalMatrix * bitangent);
vec3 N = normalize(normalMatrix * normal);
vs_out.TBN = mat3(T, B, N);

vs_out.Tangent = T;
vs_out.Bitangent = B;
}

Фрагмент

#version 330 core
out vec4 FragColor;

in VS_OUT {
vec3 FragPos;
vec3 Normal;
vec2 TexCoords;
vec3 Tangent;
vec3 Bitangent;
mat3 TBN;
} fs_in;

uniform sampler2D diffuseMap;
uniform sampler2D normalMap;

uniform vec3 lightPos;
uniform vec3 viewPos;

uniform bool normalMapping;

void main()
{
vec3 normal = fs_in.Normal;
mat3 tbn;
if(normalMapping)
{
// Obtain normal from normal map in range [0,1]
normal = texture(normalMap, fs_in.TexCoords).rgb;
// Transform normal vector to range [-1,1]
normal = normalize(normal * 2.0 - 1.0);
// Then transform normal in tangent space to world-space via TBN matrix
tbn = mat3(fs_in.Tangent, fs_in.Bitangent, fs_in.Normal); // TBN calculated in fragment shader
// normal = normalize(tbn * normal); // This works!
normal = normalize(fs_in.TBN * normal); // This gives incorrect results
}

// Get diffuse color
vec3 color = texture(diffuseMap, fs_in.TexCoords).rgb;
// Ambient
vec3 ambient = 0.1 * color;
// Diffuse
vec3 lightDir = normalize(lightPos - fs_in.FragPos);
float diff = max(dot(lightDir, normal), 0.0);
vec3 diffuse = diff * color;
// Specular
vec3 viewDir = normalize(viewPos - fs_in.FragPos);
vec3 reflectDir = reflect(-lightDir, normal);
vec3 halfwayDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(normal, halfwayDir), 0.0), 32.0);

vec3 specular = vec3(0.2) * spec; // assuming bright white light color
FragColor = vec4(ambient + diffuse + specular, 1.0f);
FragColor = vec4(normal, 1.0); // display normals for debugging
}

Обе матрицы TBN явно отличаются. Ниже я скомпилировал изображение с различными выводами фрагмента шейдера:

нормальное отображение разных выходов

Вы можете видеть, что векторы T, B и N верны, как и фрагментный шейдер tbn матрица, но матрица TBN от вершинного шейдера fs_in.TBN дает полностью фиктивные значения.

Я совершенно не понимаю, почему это не работает. Я знаю, что могу просто передать вектор Tangent и Bitangent фрагментному шейдеру, вычислить его там и покончить с этим, но мне довольно любопытно узнать точную причину, почему это не работает?

4

Решение

Некоторые общие замечания:

1) Вы должны нормализовать fs_in.Tangent, fs_in.Bitangent, а также fs_in.Normal в фрагментном шейдере, поскольку не гарантируется, что они будут после различной интерполяции. Они должны быть нормализованы, потому что они являются базисными векторами системы координат.

2) Вам не нужно проходить все три, касательную, битовую и нормальную, поскольку одну из них можно вычислить с использованием перекрестного произведения: bitangent = cross(tangent, normal), Эта точка также говорит в пользу прохождения (двух) векторов вместо всей матрицы.

На ваш вопрос, почему fs_in.TBN не похож tbn во фрагментном шейдере:

Изображения, которые вы предоставляете, действительно выглядят очень странно. Похоже, векторы матрицы как-то перепутаны. Какой вывод вы получаете, отображая транспонированную матрицу transpose(fs_in.TBN)?

Также убедитесь, что столбцы матрицы доступны правильно, как описано в [1]! (Похоже, что они есть, но, пожалуйста, проверьте еще раз, вы никогда не знаете.)

[1] Geeks3D; Матричные поля GLSL 4 × 4; http://www.geeks3d.com/20141114/glsl-4×4-matrix-mat4-fields/

1

Другие решения