法线贴图问题
Normal Mapping Issues
我第一次尝试在我的 glsl 着色器中实现法线贴图。我已经编写了一个计算切线和双切线的 ObjLoader - 然后我将相关信息传递给我的着色器(稍后我将展示代码)。但是,当我 运行 程序时,我的模型最终看起来像这样:
看起来不错,我知道,但不是我想要实现的目标!
我知道我应该简单地计算方向向量而不是移动顶点 - 但似乎在某个地方我最终犯了那个错误。
我不确定是我在读取 .obj 文件和计算 tangent/bitangent 向量时犯了错误,还是错误发生在我的 Vertex/Fragment 着色器中。
现在我的代码:
在我的 ObjLoader 中 - 当我遇到一张脸时,我会为脸的所有三个顶点计算 deltaPositions 和 deltaUv 向量 - 然后计算切线和副切线向量:
然后我整理收集到的顶点数据以构建我的索引列表 - 在该过程中我重构切线和副切线向量以尊重新构建的索引列表。
最后 - 我执行正交化并计算最终的双切线向量。
绑定VAO、VBO、IBO后,分别传递所有信息——我的shader计算如下:
顶点着色器:
void main()
{
// Output position of the vertex, in clip space
gl_Position = MVP * vec4(pos, 1.0);
// Position of the vertex, in world space
v_Position = (M * vec4(pos, 0.0)).xyz;
vec4 bitan = V * M * vec4(bitangent, 0.0);
vec4 tang = V * M * vec4(tangent, 0.0);
vec4 norm = vec4(normal, 0.0);
mat3 TBN = transpose(mat3(tang.xyz, bitan.xyz, norm.xyz));
// Vector that goes from the vertex to the camera, in camera space
vec3 vPos_cameraspace = (V * M * vec4(pos, 1.0)).xyz;
camdir_cameraspace = normalize(-vPos_cameraspace);
// Vector that goes from the vertex to the light, in camera space
vec3 lighPos_cameraspace = (V * vec4(lightPos_worldspace, 0.0)).xyz;
lightdir_cameraspace = normalize((lighPos_cameraspace - vPos_cameraspace));
v_TexCoord = texcoord;
lightdir_tangentspace = TBN * lightdir_cameraspace;
camdir_tangentspace = TBN * camdir_cameraspace;
}
片段着色器:
void main()
{
// Light Emission Properties
vec3 LightColor = (CalcDirectionalLight()).xyz;
float LightPower = 20.0;
// Cutting out texture 'black' areas of texture
vec4 tempcolor = texture(AlbedoTexture, v_TexCoord);
if (tempcolor.a < 0.5)
discard;
// Material Properties
vec3 MaterialDiffuseColor = tempcolor.rgb;
vec3 MaterialAmbientColor = material.ambient * MaterialDiffuseColor;
vec3 MaterialSpecularColor = vec3(0, 0, 0);
// Local normal, in tangent space
vec3 TextureNormal_tangentspace = normalize(texture(NormalTexture, v_TexCoord)).rgb;
TextureNormal_tangentspace = (TextureNormal_tangentspace * 2.0) - 1.0;
// Distance to the light
float distance = length(lightPos_worldspace - v_Position);
// Normal of computed fragment, in camera space
vec3 n = TextureNormal_tangentspace;
// Direction of light (from the fragment)
vec3 l = normalize(TextureNormal_tangentspace);
// Find angle between normal and light
float cosTheta = clamp(dot(n, l), 0, 1);
// Eye Vector (towards the camera)
vec3 E = normalize(camdir_tangentspace);
// Direction in which the triangle reflects the light
vec3 R = reflect(-l, n);
// Find angle between eye vector and reflect vector
float cosAlpha = clamp(dot(E, R), 0, 1);
color =
MaterialAmbientColor +
MaterialDiffuseColor * LightColor * LightPower * cosTheta / (distance * distance) +
MaterialSpecularColor * LightColor * LightPower * pow(cosAlpha, 5) / (distance * distance);
}
我可以在您的代码中发现 1 个明显的错误。 TBN
由 bitangent
、tangent
和 normal
生成。虽然 bitangent
和 tangent
从模型 space 转换为视图 space,但 normal
未转换。那没有任何意义。所有 3 个矢量都必须与同一坐标系相关:
vec4 bitan = V * M * vec4(bitangent, 0.0);
vec4 tang = V * M * vec4(tangent, 0.0);
vec4 norm = V * M * vec4(normal, 0.0);
mat3 TBN = transpose(mat3(tang.xyz, bitan.xyz, norm.xyz));
我第一次尝试在我的 glsl 着色器中实现法线贴图。我已经编写了一个计算切线和双切线的 ObjLoader - 然后我将相关信息传递给我的着色器(稍后我将展示代码)。但是,当我 运行 程序时,我的模型最终看起来像这样:
看起来不错,我知道,但不是我想要实现的目标! 我知道我应该简单地计算方向向量而不是移动顶点 - 但似乎在某个地方我最终犯了那个错误。
我不确定是我在读取 .obj 文件和计算 tangent/bitangent 向量时犯了错误,还是错误发生在我的 Vertex/Fragment 着色器中。
现在我的代码:
在我的 ObjLoader 中 - 当我遇到一张脸时,我会为脸的所有三个顶点计算 deltaPositions 和 deltaUv 向量 - 然后计算切线和副切线向量:
然后我整理收集到的顶点数据以构建我的索引列表 - 在该过程中我重构切线和副切线向量以尊重新构建的索引列表。
最后 - 我执行正交化并计算最终的双切线向量。
绑定VAO、VBO、IBO后,分别传递所有信息——我的shader计算如下:
顶点着色器:
void main()
{
// Output position of the vertex, in clip space
gl_Position = MVP * vec4(pos, 1.0);
// Position of the vertex, in world space
v_Position = (M * vec4(pos, 0.0)).xyz;
vec4 bitan = V * M * vec4(bitangent, 0.0);
vec4 tang = V * M * vec4(tangent, 0.0);
vec4 norm = vec4(normal, 0.0);
mat3 TBN = transpose(mat3(tang.xyz, bitan.xyz, norm.xyz));
// Vector that goes from the vertex to the camera, in camera space
vec3 vPos_cameraspace = (V * M * vec4(pos, 1.0)).xyz;
camdir_cameraspace = normalize(-vPos_cameraspace);
// Vector that goes from the vertex to the light, in camera space
vec3 lighPos_cameraspace = (V * vec4(lightPos_worldspace, 0.0)).xyz;
lightdir_cameraspace = normalize((lighPos_cameraspace - vPos_cameraspace));
v_TexCoord = texcoord;
lightdir_tangentspace = TBN * lightdir_cameraspace;
camdir_tangentspace = TBN * camdir_cameraspace;
}
片段着色器:
void main()
{
// Light Emission Properties
vec3 LightColor = (CalcDirectionalLight()).xyz;
float LightPower = 20.0;
// Cutting out texture 'black' areas of texture
vec4 tempcolor = texture(AlbedoTexture, v_TexCoord);
if (tempcolor.a < 0.5)
discard;
// Material Properties
vec3 MaterialDiffuseColor = tempcolor.rgb;
vec3 MaterialAmbientColor = material.ambient * MaterialDiffuseColor;
vec3 MaterialSpecularColor = vec3(0, 0, 0);
// Local normal, in tangent space
vec3 TextureNormal_tangentspace = normalize(texture(NormalTexture, v_TexCoord)).rgb;
TextureNormal_tangentspace = (TextureNormal_tangentspace * 2.0) - 1.0;
// Distance to the light
float distance = length(lightPos_worldspace - v_Position);
// Normal of computed fragment, in camera space
vec3 n = TextureNormal_tangentspace;
// Direction of light (from the fragment)
vec3 l = normalize(TextureNormal_tangentspace);
// Find angle between normal and light
float cosTheta = clamp(dot(n, l), 0, 1);
// Eye Vector (towards the camera)
vec3 E = normalize(camdir_tangentspace);
// Direction in which the triangle reflects the light
vec3 R = reflect(-l, n);
// Find angle between eye vector and reflect vector
float cosAlpha = clamp(dot(E, R), 0, 1);
color =
MaterialAmbientColor +
MaterialDiffuseColor * LightColor * LightPower * cosTheta / (distance * distance) +
MaterialSpecularColor * LightColor * LightPower * pow(cosAlpha, 5) / (distance * distance);
}
我可以在您的代码中发现 1 个明显的错误。 TBN
由 bitangent
、tangent
和 normal
生成。虽然 bitangent
和 tangent
从模型 space 转换为视图 space,但 normal
未转换。那没有任何意义。所有 3 个矢量都必须与同一坐标系相关:
vec4 bitan = V * M * vec4(bitangent, 0.0);
vec4 tang = V * M * vec4(tangent, 0.0);
vec4 norm = V * M * vec4(normal, 0.0);
mat3 TBN = transpose(mat3(tang.xyz, bitan.xyz, norm.xyz));