法线贴图错误

时间:2018-11-07 17:42:56

标签: c++ opengl glsl

我正在OpenGL上创建模型加载程序。我照顾了光线和镜面反射,但仍停留在法线贴图上。我认为我在法线贴图计算中犯了一个错误。

正常图像为:

enter image description here

当我应用法线贴图效果时,它就是它的外观,这是屏幕截图:

out

我的顶点着色器:

#version 430 core

layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 uvw;
layout(location = 3) in vec3 tangent;
layout(location = 4) in vec3 biTangent;

uniform mat4 M;
uniform mat4 MVP;
uniform mat3 N;

out block
{
    vec4 position;
    vec3 normal;
    vec2 uvw;
    vec3 tangent;
    vec3 biTangent;
    mat3 TBN;
} Out;

void main()
{
    Out.position = M * vec4(position, 1.0);
    Out.normal = normalize(N * normal);
    Out.uvw = uvw;
    Out.tangent = (M * vec4(tangent, 0.0)).xyz;
    Out.TBN = mat3(tangent, biTangent, normal); 

    gl_Position = MVP * vec4(position, 1.0);
}

片段着色器是:

#version 430 core
#define M_PI        3.14159265358979323846

layout(binding = 0) uniform sampler2D dts;
layout(binding = 1) uniform sampler2D sts;
layout(binding = 2) uniform sampler2D nts;

struct Light {
    vec3 position;
    vec3 filterColor;
    float multiplier;
};

struct Material {
    vec3 baseColor;
    float baseColorMultiplier;
    float roughness;
    float ior;
};

uniform Light light;
uniform Material material;

in block
{
    vec4 position;
    vec3 normal;
    vec2 uvw;
    vec3 tangent;
    vec3 biTangent;
    mat3 TBN;
} In;

out vec4 color;


vec3 Le(Light light, vec4 position, vec3 wi) {
    vec3 Le;
    float dist = length(wi);
    Le = light.filterColor / (dist * dist);

    return Le;
}

vec3 Fresnel(vec3 spec, vec3 normal, vec3 wi)
{
    return spec + (1 - spec) * pow((1 - max(0.0, dot(wi, normal))), 5);
}

vec3 Normal()
{
    vec4 norm = texture2D(nts, In.uvw);

    return vec3(norm);
}

vec3 Diffuse(Material material, vec2 uvw) {
    vec4 diff = texture2D(dts, uvw);
    return vec3(diff) / M_PI;
}

vec3 Reflection(Material material, vec3 wi, vec3 normal, vec2 uvw)
{
    vec3 f;
    float cosTheta = dot(normal, wi);

    vec4 spec = texture2D(sts, uvw);
    f += vec3(1.0) * vec3(spec) * pow(max(0.0, abs(cosTheta)), material.roughness);

    f += Fresnel(vec3(spec), normal, wi);
    return f;
}

vec3 BRDF(Light light, Material material, vec3 wo, vec3 wi, vec4 position, vec3 normal, vec2 uvw) {
    vec3 L;

    // Evaluate emitted light
    vec3 Li = Le(light, position, wi);

    // Diffuse
    vec3 f = Diffuse(material, uvw);    

    // Reflection
    float cosThetaI = max(0.0, dot(wi, normal));
    if(cosThetaI > 0.0) {
        f += Reflection(material, wi, normal, uvw);
    }

    // BRDF function
    L += f * Li * max(0.0, dot(wi, normal));

    return L;
}

void main(void)
{   
    vec3 L;
    vec3 wi, wo;

    // Evaluate incoming and outgoing light direction
    wi = normalize(light.position - vec3(In.position));
    wo = reflect(-wi, In.normal);

    // Evaluate normal map
    vec4 normal = texture2D(nts, In.uvw);
    normal = normalize(normal * 2.0 -1.0);
    normal = vec4(normalize(In.TBN * normal.xyz), 0.0);

    L += BRDF(light, material, wo, wi, In.position, normal.xyz, In.uvw);
    color = vec4(L, 1.0);
}

我的切线和切线计算代码是:

void CGLPrimitive::CalculateTangentBiTangent()
{
    for (unsigned int i = 0; i < positions.size(); i+=3) {
        CVector3<float> p1 = positions[i];
        CVector3<float> p2 = positions[i + 1];
        CVector3<float> p3 = positions[i + 2];

        CVector2<float> uv1 = uv[i];
        CVector2<float> uv2 = uv[i+1];
        CVector2<float> uv3 = uv[i+2];


        CVector3<float> e1 = p2 - p1;
        CVector3<float> e2 = p3 - p1;

        CVector2<float> deltaUV1 = uv2 - uv1;
        CVector2<float> deltaUV2 = uv3 - uv1;

        float f = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV2.x * deltaUV1.y);


        CVector3<float> t;
        t.x = f * (deltaUV2.y * e1.x - deltaUV1.y * e2.x);
        t.y = f * (deltaUV2.y * e1.y - deltaUV1.y * e2.y);
        t.z = f * (deltaUV2.y * e1.z - deltaUV1.y * e2.z);
        t = Normalize(t);
        tangents.push_back(t);
        tangents.push_back(t);
        tangents.push_back(t);

        CVector3<float> b;
        b.x = f * (-deltaUV2.x * e1.x + deltaUV1.x * e2.x);
        b.y = f * (-deltaUV2.x * e1.y + deltaUV1.x * e2.y);
        b.z = f * (-deltaUV2.x * e1.z + deltaUV1.x * e2.z);
        b = Normalize(b);
        biTangents.push_back(b);
        biTangents.push_back(b);
        biTangents.push_back(b);
    }


}

我想知道我在哪里犯错。谢谢。

2 个答案:

答案 0 :(得分:1)

tangentbiTangent必须像使用normal向量那样通过常规矩阵进行转换:

Out.tangent   = normalize(N * tangent); 
Out.biTangent = normalize(N * biTangent);
Out.normal    = normalize(N * normal);

在片段着色器中,计算是在世界空间中完成的。为此,您必须将法线贴图的法线向量转换为世界空间。

mat3(tangent, biTangent, normal)是从纹理空间转换为模型空间的矩阵。

从纹理空间转换为世界空间的矩阵是:

Out.TBN = N * mat3(tangent, biTangent, normal);  

Out.TBN = mat3(Out.tangent, Out.biTangent, Out.normal);       

使用以上切线空间矩阵的计算来解决您的问题。

答案 1 :(得分:1)

几件事:

1)正如Rabbid76所说,您必须将切线和二进制切线相乘。您可以使用modelview矩阵,因为这些向量不会因比例尺不均匀而遭受任何失真。

2)我们不知道GPU中纹理的格式。如果它的Alpha通道设置为1.0,然后将整个矢量归一化:

|V|

归一化的矢量可能会在4D中进行归一化,但可能不会在3D中进行标准化。只需从纹理中提取rgb即可使用。

vec4 normal = texture2D(nts, In.uvw);
normal = normalize(normal * 2.0 -1.0); 

3)这是做什么用的? vec3 normal = texture2D(nts, In.uvw).rgb; 我相信这是您问题的根源,因为您以错误的方式平滑法线。示例:

normal = normal * 0.5 + 0.5;将成为normal (1,0,0)

但是

(1,0.5,0.5)将成为normal (-1,0,0)

我还建议您通过绘制法线,切线和双切线(例如,使用几何着色器)来调试它们,以确保它们正确,从而调试它们。