【问题标题】:Normal mapping: TBN matrix different result in vertex shader compared to fragment shader法线映射:与片段着色器相比,TBN 矩阵不同导致顶点着色器
【发布时间】:2015-08-04 21:26:39
【问题描述】:

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

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

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

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

顶点:

#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 矩阵也是正确的,但是来自顶点着色器 fs_in.TBN 的 TBN 矩阵给出了完全虚假的值。

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

【问题讨论】:

  • 我看不出任何明显的原因导致它不起作用(我在我的项目的顶点着色器中计算 TBN,所以这绝对是可能的)。我想知道的是,如果顶点着色器输出可以读回顶点着色器?如果您像这样更改代码,是否有帮助: mat3 normalMatrix = transpose(inverse(mat3(model))); 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; vs_out.Normal = N;
  • 是的,我想不出任何可能导致这种行为的东西。应该允许使用vec3 N = vs_out.Normal;,但为了安全起见,我使用了安全版本。不幸的是,它仍然给出相同的结果
  • 不确定你是否应该只删除FragPosw 坐标 - 你应该投影坐标。即使这与您的问题无关。
  • @BrettHale FragPos 位于世界空间中,因此仅由 model 矩阵转换。影响w 坐标的唯一变换矩阵是projection 矩阵,因此在这种情况下FagPos.w 将始终为1.0,并且不需要透视变换。

标签: c++ opengl glsl bump-mapping


【解决方案1】:

一些一般性说明:

1) 您应该在片段着色器中标准化fs_in.Tangentfs_in.Bitangentfs_in.Normal,因为不能保证它们在变化插值之后。它们应该被归一化,因为它们是坐标系的基向量。

2) 您不需要传递所有三个,切线、双切线和法线,因为可以使用叉积计算其中之一:bitangent = cross(tangent, normal)。这一点也支持传递(两个)向量而不是整个矩阵。

对于你的问题,为什么fs_in.TBN 在片段着色器中看起来不像tbn

确实,您提供的图像看起来很奇怪。看起来矩阵的向量以某种方式混合在一起。你得到哪个输出,显示转置矩阵transpose(fs_in.TBN)

还要确保矩阵的列被正确访问,如 [1] 中所述! (看起来他们是,但请仔细检查,你永远不知道。)

[1] 极客3D; GLSL 4×4 矩阵字段; http://www.geeks3d.com/20141114/glsl-4x4-matrix-mat4-fields/

【讨论】:

    猜你喜欢
    • 2019-07-31
    • 2011-05-24
    • 1970-01-01
    • 2019-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多