法线贴图:与片段着色器相比,TBN矩阵在顶点着色器中的结果不同

时间:2015-05-22 18:41:40

标签: c++ opengl glsl bump-mapping

我正在研究教程的正常映射实现,为了教学目的,我想将TBN矩阵传递给片段着色器(来自顶点着色器),这样我就可以将切线空间中的法向量转换为世界 - 照明计算空间。法线贴图应用于2D平面,其法线指向正z方向。

但是,当我在平面的顶点着色器中计算TBN矩阵时(因此所有顶点的所有切线/位角都相同),显示的法线完全关闭。如果我将切线/比特和法线向量传递给片段着色器并在那里构建TBN,它可以正常工作,如下图所示(显示法线):

normal mapping wrong

这是奇怪的地方。因为平面是平坦的,所以它的所有顶点的T,B和N矢量都是相同的,因此TBN矩阵对于每个片段也应该是相同的(因为片段插值不会改变任何东西)。顶点着色器中的TBN矩阵应与片段着色器中的TBN矩阵完全相同,但视觉输出则另有说明。

顶点和片段着色器的源代码如下:

顶点:

[~,~,C] = something(X)

片段

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

两个TBN矩阵明显不同。下面我编译了不同片段着色器输出的图像:

normal mapping different outputs

你可以看到T,B和N向量是正确的,片段着色器的#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的TBN矩阵给出了完全虚假的值。

我完全不知道为什么它不起作用。我知道我可以简单地将Tangent和Bitangent向量传递给片段着色器,在那里计算它并完成它但我很好奇为什么这不起作用的确切原因?

1 个答案:

答案 0 :(得分:1)

一些一般性评论:

1)您应该在片段着色器中规范化fs_in.Tangentfs_in.Bitangentfs_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-4x4-matrix-mat4-fields/