GLSL3切线空间坐标和法线贴图
首先,我必须道歉在这个问题上发布另一个问题(已经有很多了!)。我确实在寻找其他相关的问题和答案,但不幸的是他们中没有人向我展示解决方案。现在我绝望了! :DGLSL3切线空间坐标和法线贴图
值得一提的是,下面的代码给出了令人满意的'颠簸'效果。这似乎是错误的场景启发。
现场:死了简单!中心的立方体,围绕它旋转的光源(与地面平行)及以上。
我的做法是从我的基本光着色器开始,它给了我足够的输出(或者我认为!)。第一步是修改它在切线空间中进行计算,然后使用从纹理中提取的法线。
我想很好地评论的代码,但在短期我有两个问题:
1)只做基本的照明(没有法线贴图),我希望来现场看完全一样,有或而无需用TBN矩阵将我的向量转换为切线空间。我错了吗?
2)为什么我得到不正确的启示?
一些截图给你一个想法(编辑) - 遵循LJ的评论,我不再总结法线和每个顶点/面的切线。有趣的是,它突出了这个问题(请参阅捕获,我已经标记了光线如何移动)。
基本上它是因为如果立方体旋转90度到左侧,或者,如如果光被图灵垂直代替正常映射水平
结果的:
版本以简单的光:
顶点着色器:
// Information about the light.
// Here we care essentially about light.Position, which
// is set to be something like vec3(cos(x)*9, 5, sin(x)*9)
uniform Light_t Light;
uniform mat4 W; // The model transformation matrix
uniform mat4 V; // The camera transformation matrix
uniform mat4 P; // The projection matrix
in vec3 VS_Position;
in vec4 VS_Color;
in vec2 VS_TexCoord;
in vec3 VS_Normal;
in vec3 VS_Tangent;
out vec3 FS_Vertex;
out vec4 FS_Color;
out vec2 FS_TexCoord;
out vec3 FS_LightPos;
out vec3 FS_ViewPos;
out vec3 FS_Normal;
// This method calculates the TBN matrix:
// I'm sure it is not optimized vertex shader code,
// to have this seperate method, but nevermind for now :)
mat3 getTangentMatrix()
{
// Note: here I must say am a bit confused, do I need to transform
// with 'normalMatrix'? In practice, it seems to make no difference...
mat3 normalMatrix = transpose(inverse(mat3(W)));
vec3 norm = normalize(normalMatrix * VS_Normal);
vec3 tang = normalize(normalMatrix * VS_Tangent);
vec3 btan = normalize(normalMatrix * cross(VS_Normal, VS_Tangent));
tang = normalize(tang - dot(tang, norm) * norm);
return transpose(mat3(tang, btan, norm));
}
void main()
{
// Set the gl_Position and pass color + texcoords to the fragment shader
gl_Position = (P * V * W) * vec4(VS_Position, 1.0);
FS_Color = VS_Color;
FS_TexCoord = VS_TexCoord;
// Now here we start:
// This is where supposedly, multiplying with the TBN should not
// change anything to the output, as long as I apply the transformation
// to all of them, or none.
// Typically, removing the 'TBN *' everywhere (and not using the normal
// texture later in the fragment shader) is exactly the code I use for
// my basic light shader.
mat3 TBN = getTangentMatrix();
FS_Vertex = TBN * (W * vec4(VS_Position, 1)).xyz;
FS_LightPos = TBN * Light.Position;
FS_ViewPos = TBN * inverse(V)[3].xyz;
// This line is actually not needed when using the normal map:
// I keep the FS_Normal variable for comparison purposes,
// when I want to switch to my basic light shader effect.
// (see later in fragment shader)
FS_Normal = TBN * normalize(transpose(inverse(mat3(W))) * VS_Normal);
}
而片段着色器:
struct Textures_t
{
int SamplersCount;
sampler2D Samplers[4];
};
struct Light_t
{
int Active;
float Ambient;
float Power;
vec3 Position;
vec4 Color;
};
uniform mat4 W;
uniform mat4 V;
uniform Textures_t Textures;
uniform Light_t Light;
in vec3 FS_Vertex;
in vec4 FS_Color;
in vec2 FS_TexCoord;
in vec3 FS_LightPos;
in vec3 FS_ViewPos;
in vec3 FS_Normal;
out vec4 frag_Output;
vec4 getPixelColor()
{
return Textures.SamplersCount >= 1
? texture2D(Textures.Samplers[0], FS_TexCoord)
: FS_Color;
}
vec3 getTextureNormal()
{
// FYI: the normal texture is always at index 1
vec3 bump = texture(Textures.Samplers[1], FS_TexCoord).xyz;
bump = 2.0 * bump - vec3(1.0, 1.0, 1.0);
return normalize(bump);
}
vec4 getLightColor()
{
// This is the one line that changes between my basic light shader
// and the normal mapping one:
// - If I don't do 'TBN *' earlier and use FS_Normal here,
// the enlightenment seems fine (see second screenshot)
// - If I do multiply by TBN (including on FS_Normal), I would expect
// the same result as without multiplying ==> not the case: it looks
// very similar to the result with normal mapping
// (just has no bumpy effect of course)
// - If I use the normal texture (along with TBN of course), then I get
// the result you see in the first screenshot.
vec3 N = getTextureNormal(); // Instead of 'normalize(FS_Normal);'
// Everything from here on is the same as my basic light shader
vec3 L = normalize(FS_LightPos - FS_Vertex);
vec3 E = normalize(FS_ViewPos - FS_Vertex);
vec3 R = normalize(reflect(-L, N));
// Ambient color: light color times ambient factor
vec4 ambient = Light.Color * Light.Ambient;
// Diffuse factor: product of Normal to Light vectors
// Diffuse color: light color times the diffuse factor
float dfactor = max(dot(N, L), 0);
vec4 diffuse = clamp(Light.Color * dfactor, 0, 1);
// Specular factor: product of reflected to camera vectors
// Note: applies only if the diffuse factor is greater than zero
float sfactor = 0.0;
if(dfactor > 0)
{
sfactor = pow(max(dot(R, E), 0.0), 8.0);
}
// Specular color: light color times specular factor
vec4 specular = clamp(Light.Color * sfactor, 0, 1);
// Light attenuation: square of the distance moderated by light's power factor
float atten = 1 + pow(length(FS_LightPos - FS_Vertex), 2)/Light.Power;
// The fragment color is a factor of the pixel and light colors:
// Note: attenuation only applies to diffuse and specular components
return getPixelColor() * (ambient + (diffuse + specular)/atten);
}
void main()
{
frag_Output = Light.Active == 1
? getLightColor()
: getPixelColor();
}
这就是它!我希望你有足够的信息,当然,你的帮助将不胜感激! :) 保重。
我experiancing一个非常类似的问题,我无法解释为什么的照明不工作的权利,但我可以回答你的第一个问题,起码说明如何我不知怎么照明可接受工作(尽管你的问题可能并不一定与我的相同)。
首先在理论上如果切线和bitangents计算正确,那么你在tangentspace与tangentspace正常[0,0,1]做计算的时候应该得到完全一样的照明效果。其次,虽然你应该通过乘以逆转矩模型 - 视图矩阵as explained by this tutorial将你的法线从模型转换到相机空间是常识,我发现如果你转换正常相切模型视图矩阵而不是逆转置模型视图。即使用normalMatrix = mat3(W);
而不是normalMatrix = transpose(inverse(mat3(W)));
。
在我的情况下,这确实“修复”与光的问题,,但我不知道为什么这固定它,但我不能保证它不(它实际上我认为它)介绍其他阴影问题
没有看你的代码,从图像看,你的立方体看起来像8个共享顶点和gouraud法线?所以你的法线和你的切线是错误的,高斯阴影用于近似光滑的表面,立方体当然不是光滑的表面。在你做任何事情之前,修复你的表面法线(提示它们应该垂直于表面,额外的提示:是的,这意味着没有共享顶点)。 –
非常感谢LJ - gouraud法线不是问题的根源,但是您的评论使问题更加明显我发现:现在左边的脸总是处于光明中,就好像它在上面 - 右边的那个(截图中不可见)总是在黑暗中! – Smoove