在openGL中使用光照进行法线贴图

时间:2016-11-14 12:56:24

标签: opengl lighting shading

我正在尝试在片段着色器中实现法线贴图和光照。但我的代码似乎不起作用。我的想法是将一个额外的ModelMatrix副本传递给片段着色器,以便它可以转换存储在纹理中的法线。但反射光实际上发生在另一边。

[更新] 在得到一些建议之后,我在顶点着色器中计算了TBN矩阵,将TBN基础转换为世界空间,将lightPosition和eyePosition转换为tangentSpace。但这次这个星球甚至都没有发光!仅显示环境光。然后我发现这是因为漫反射组件评估为负值,然后将其钳制为0.我似乎无法在着色器中找到错误。

VertexShader:

#version 400

in layout(location=0) vec3 vertexPosition;
in layout(location=1) vec2 vertexUV;
in layout(location=2) vec3 vertexNormal;
in layout(location=3) vec3 vertexTangent;
in layout(location=4) vec3 vertexBitangent;

out vec2 UV;
out vec3 vertexPositionWorld;
out vec3 tangentLightPos;
out vec3 tangentViewPos;
out vec3 tangentFragPos;

uniform vec3 lightPositionWorld;
uniform vec3 eyePositionWorld;

uniform mat4 M;
uniform mat4 V;
uniform mat4 P;

void main()
{
gl_Position = P * V * M * vec4(vertexPosition, 1.0f);
vertexPositionWorld = vec3(M * vec4(vertexPosition, 1.0f));
UV = vertexUV;

// note scaling problem here 
mat3 normalMatrix = transpose(inverse(mat3(M)));
// mat3 normalMatrix = inverse(transpose(mat3(M)));
vec3 T = normalize(normalMatrix * vertexTangent);
vec3 B = normalize(normalMatrix * vertexBitangent);
vec3 N = normalize(normalMatrix * vertexNormal);

mat3 TBN = transpose(mat3(T, B, N));
tangentLightPos = TBN * lightPositionWorld;
tangentViewPos = TBN * eyePositionWorld;
tangentFragPos = TBN * vertexPositionWorld;
}

FragmentShader:

#version 400

uniform sampler2D textureSampler_1;
uniform sampler2D textureSampler_2;
uniform vec3 AmbientLightPower;
uniform vec3 DiffuseLightPower;
uniform vec3 SpecularLightPower;
uniform float specularLightPower;

in vec2 UV;
in vec3 vertexPositionWorld;
in vec3 tangentLightPos;
in vec3 tangentViewPos;
in vec3 tangentFragPos;

out vec4 finalColor;

void main()
{
vec3 normal = texture(textureSampler_2, UV).rgb;
normal = normalize(normal * 2.0 - 1.0);

vec3 MaterialAmbientColor = texture( textureSampler_1, UV ).rgb;
vec3 MaterialDiffuseColor = texture( textureSampler_1, UV ).rgb;
vec3 MaterialSpecularColor = vec3(0.3,0.3,0.3);
// diffuse light
vec3 lightDirection = normalize(tangentLightPos - tangentFragPos);
float DiffuseBrightness = clamp(dot(lightDirection, normal), 0, 1);

// specular light
vec3 reflectedDirection = normalize(reflect(-lightDirection, normal));
vec3 viewDirection = normalize(tangentViewPos - tangentFragPos);
float SpecularBrightness = clamp(dot(reflectedDirection, viewDirection), 0, 1);

finalColor = vec4(

MaterialAmbientColor  * AmbientLightPower +

MaterialDiffuseColor  * DiffuseLightPower * DiffuseBrightness +

MaterialSpecularColor * SpecularLightPower * pow(SpecularBrightness, specularLightPower), 1.0f);
}

1 个答案:

答案 0 :(得分:2)

您的方法是采用法线贴图,并通过模型矩阵进行转换。仅当法线贴图是世界空间法线贴图时,此选项才有效。很少使用世界空间法线贴图,通常法线贴图是切线空间法线贴图。如果你不确定你有哪种类型的法线贴图,那么你可以检查纹理 - 如果它看起来大部分是蓝色,那么你很可能有一个切线空间法线贴图,如果它非常多彩,那么它可能是世界空间和我的回答对你不对。

要使用切线空间法线贴图,您需要做更多的工作。通常,在顶点着色器中,您可以获得一些基矢量(Normal,Binormal和Tangent aka NBT),并使用这些矢量将法线贴图转换为世界空间,或将光线位置转换为切线空间(后者通常是因为你在顶点着色器中做了更多工作,所以对于少量灯光效率更高。)

切线空间很难让你头脑清醒,我建议稍微阅读一下这个主题。 This article可能是一个不错的开始。