切线空间法线贴图 - 着色器完整性检查
Posted
技术标签:
【中文标题】切线空间法线贴图 - 着色器完整性检查【英文标题】:Tangent Space Normal Mapping - shader sanity check 【发布时间】:2012-04-05 15:33:35 【问题描述】:我从我的切线空间法线贴图着色器中得到了一些非常奇怪的结果:)。在我在这里展示的场景中,茶壶和方格墙正在使用我的普通 Phong-Blinn 着色器进行着色(显然,茶壶背面剔除使它具有轻微短暂的外观和感觉 :-))。我尝试将法线映射添加到球体中,结果令人迷幻:
光线来自右侧(几乎可以看到黑色斑点)。我在球体上使用的法线贴图如下所示:
我正在使用 AssImp 处理输入模型,因此它会自动为我计算每个顶点的切线和双法线。
像素和顶点着色器如下。我不太确定出了什么问题,但如果切线基矩阵有某种错误,我也不会感到惊讶。我假设我必须将事物计算到眼睛空间,然后将眼睛和光矢量转换到切线空间,这是正确的方法。请注意,灯光位置已经进入了视图空间中的着色器。
// Vertex Shader
#version 420
// Uniform Buffer Structures
// Camera.
layout (std140) uniform Camera
mat4 Camera_Projection;
mat4 Camera_View;
;
// Matrices per model.
layout (std140) uniform Model
mat4 Model_ViewModelSpace;
mat4 Model_ViewModelSpaceInverseTranspose;
;
// Spotlight.
layout (std140) uniform OmniLight
float Light_Intensity;
vec3 Light_Position; // Already in view space.
vec4 Light_Ambient_Colour;
vec4 Light_Diffuse_Colour;
vec4 Light_Specular_Colour;
;
// Streams (per vertex)
layout(location = 0) in vec3 attrib_Position;
layout(location = 1) in vec3 attrib_Normal;
layout(location = 2) in vec3 attrib_Tangent;
layout(location = 3) in vec3 attrib_BiNormal;
layout(location = 4) in vec2 attrib_Texture;
// Output streams (per vertex)
out vec3 attrib_Fragment_Normal;
out vec4 attrib_Fragment_Position;
out vec3 attrib_Fragment_Light;
out vec3 attrib_Fragment_Eye;
// Shared.
out vec2 varying_TextureCoord;
// Main
void main()
// Compute normal.
attrib_Fragment_Normal = (Model_ViewModelSpaceInverseTranspose * vec4(attrib_Normal, 0.0)).xyz;
// Compute position.
vec4 position = Model_ViewModelSpace * vec4(attrib_Position, 1.0);
// Generate matrix for tangent basis.
mat3 tangentBasis = mat3( attrib_Tangent,
attrib_BiNormal,
attrib_Normal);
// Light vector.
attrib_Fragment_Light = tangentBasis * normalize(Light_Position - position.xyz);
// Eye vector.
attrib_Fragment_Eye = tangentBasis * normalize(-position.xyz);
// Return position.
gl_Position = Camera_Projection * position;
...像素着色器如下所示:
// Pixel Shader
#version 420
// Samplers
uniform sampler2D Map_Normal;
// Global Uniforms
// Material.
layout (std140) uniform Material
vec4 Material_Ambient_Colour;
vec4 Material_Diffuse_Colour;
vec4 Material_Specular_Colour;
vec4 Material_Emissive_Colour;
float Material_Shininess;
float Material_Strength;
;
// Spotlight.
layout (std140) uniform OmniLight
float Light_Intensity;
vec3 Light_Position;
vec4 Light_Ambient_Colour;
vec4 Light_Diffuse_Colour;
vec4 Light_Specular_Colour;
;
// Input streams (per vertex)
in vec3 attrib_Fragment_Normal;
in vec3 attrib_Fragment_Position;
in vec3 attrib_Fragment_Light;
in vec3 attrib_Fragment_Eye;
// Shared.
in vec2 varying_TextureCoord;
// Result
out vec4 Out_Colour;
// Main
void main(void)
// Compute normals.
vec3 N = normalize(texture(Map_Normal, varying_TextureCoord).xyz * 2.0 - 1.0);
vec3 L = normalize(attrib_Fragment_Light);
vec3 V = normalize(attrib_Fragment_Eye);
vec3 R = normalize(-reflect(L, N));
// Compute products.
float NdotL = max(0.0, dot(N, L));
float RdotV = max(0.0, dot(R, V));
// Compute final colours.
vec4 ambient = Light_Ambient_Colour * Material_Ambient_Colour;
vec4 diffuse = Light_Diffuse_Colour * Material_Diffuse_Colour * NdotL;
vec4 specular = Light_Specular_Colour * Material_Specular_Colour * (pow(RdotV, Material_Shininess) * Material_Strength);
// Final colour.
Out_Colour = ambient + diffuse + specular;
编辑:场景的 3D Studio 渲染(显示 UV 在球体上正常):
【问题讨论】:
【参考方案1】:我认为你的着色器没问题,但球体上的纹理坐标完全关闭。就好像它们沿着经度向两极扭曲。
【讨论】:
谢谢。我添加了场景的 3D Studio 渲染以显示球体上的 UV 一切正常。它也使用相同的地图进行凹凸。 像往常一样,你给了我一个关于 datenwolf 答案的好线索。使用 color = (u, v, 0, 1) 渲染球体,在导入此网格时肯定没有正确处理 UV,或者我的着色器没有正确绑定。 就是这样。需要顶点着色器:varying_TextureCoord = attrib_Texture;。如此愚蠢的错误和准备问题所花费的时间:-)。 我赞成你的问题,因为它实际上是一个有用的例子(几乎)工作代码:) 是的,非常好和鼓舞人心;几乎有人会提名“本月最佳问题”(在其类别中)或类似的:)以上是关于切线空间法线贴图 - 着色器完整性检查的主要内容,如果未能解决你的问题,请参考以下文章