GLSL3切线空间坐标和法线贴图

时间:2016-09-14 22:45:31

标签: glsl shader texture-mapping light bump-mapping

首先,我必须为发布关于这个主题的另一个问题而道歉(已经有很多了!)。我确实搜索了其他相关的问题和答案,但不幸的是,他们都没有向我展示解决方案。现在我绝望了! :d

值得一提的是,下面发布的代码给出了一个令人满意的'颠簸'影响。现场启蒙似乎是错误的。

场景:很简单!中心的立方体,一个围绕它旋转的光源(平行于地面)及其上方。

我的方法是从我的基本灯光着色器开始,这给了我足够的输出(或者我认为!)。第一步是修改它以在切线空间中进行计算,然后使用从纹理中提取的法线。

我试图很好地评论代码,但总之我有两个问题

1)只进行基本照明(没有法线贴图),我希望场景看起来完全一样,无论是否使用TBN矩阵将我的矢量转换为切线空间。我错了吗?

2)为什么我得到不正确的启示?

一些截图给你一个想法(编辑) - 按照LJ的评论,我不再是法线和每个顶点/面的切线。有趣的是,它突出了这个问题(参见捕获,我已经标记了光的移动方式)。

  

基本上就好像立方体向左旋转了90度,或者就像光线垂直而不是水平地移动一样

具有法线贴图的结果:

Result with normal mapping

光线简单的版本:

Version with simple light

顶点着色器:

// 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();
}

那就是它!我希望您有足够的信息,当然,我们将非常感谢您的帮助! :)保重。

1 个答案:

答案 0 :(得分:0)

我正在尝试一个非常相似的问题,我无法解释为什么灯光无法正常工作,但我可以回答你的第一个问题,至少解释如何< / em>我以某种方式让照明工作可以接受(虽然你的问题可能不一定是我的问题)。

首先在理论上如果正确计算切线和比特数,那么在tangentspace中使用tangentspace normal [0,0,1]进行计算时,应该得到完全相同的光照结果。 / p>

其次,虽然众所周知你应该通过乘以逆转置模型 - 视图矩阵as explained by this tutorial将法线从模型转换到相机空间,但我发现如果你转换错误的灯光问题可以解决通过模型视图矩阵而不是逆转置模型视图转换法向切线。即使用normalMatrix = mat3(W);代替normalMatrix = transpose(inverse(mat3(W)));

在我的情况下,这确实“修复”了光的问题,但我不知道为什么这个修复它,但我不能保证它没有(事实上我认为它确实)介绍着色的其他问题