Неверное нормальное отображение - PullRequest
0 голосов
/ 07 ноября 2018

Я создаю программу загрузки моделей на 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 голосов
/ 08 ноября 2018

Мало вещей:

1) Как сказал Rabbid76, вы должны умножить касательные и битовые объекты. Вы можете использовать матрицу просмотра модели, поскольку эти векторы не страдают от каких-либо искажений, вызванных неравномерным масштабом.

2) Мы не знаем формат вашей текстуры в GPU. Если для него установлен альфа-канал 1.0, а затем нормализуется весь вектор:

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

Нормализованный вектор может быть нормализован в 4D, но не может быть в 3D. Просто возьмите RGB из текстуры и используйте его.

vec3 normal = texture2D(nts, In.uvw).rgb;

3) Для чего это нужно? normal = normal * 0.5 + 0.5; Я полагаю, что это корень вашей проблемы, поскольку вы сглаживаете нормали неправильно. Пример:

normal (1,0,0) станет (1,0.5,0.5)

но

normal (-1,0,0) станет (0,0.5,0.5)

Я бы также порекомендовал вам отладить ваши нормали, касательные и битовые касания, нарисовав их (например, с помощью геометрического шейдера), чтобы убедиться, что они правильные.

0 голосов
/ 08 ноября 2018

tangent и biTangent должны быть преобразованы нормальной матрицей, как вы делаете это с вектором 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);       

Используйте один из приведенных выше расчетов матрицы касательного пространства для решения вашей проблемы.

...