GLSL3 Касательные пространственные координаты и нормальное отображение

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

Стоит отметить, что приведенный ниже код дает удовлетворительный эффект "ухабистости". Это просветление сцены, которое кажется неправильным.

Сцена: смертельно просто! Куб в центре, вращающийся вокруг него источник света (параллельно земле) и выше.

Мой подход заключается в том, чтобы начать с моего базового легкого шейдера, который дает мне адекватные результаты (или я так думаю!). Первый шаг - изменить его, чтобы выполнить вычисления в касательном пространстве, а затем использовать нормаль, извлеченную из текстуры.

Я попытался красиво прокомментировать код, но вкратце у меня есть два вопроса:

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

2) Почему я получаю неправильное просветление?

Несколько скриншотов, чтобы дать вам представление (РЕДАКТИРОВАНИЕ) - после комментария ЖЖ я больше не суммирую нормали и касательные для каждой вершины / грани. Интересно, что это выдвигает на первый план проблему (см. На захвате, я отметил, как движется свет).

По сути, это так, как если бы куб был повернут на 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();
}

Это оно! Я надеюсь, что у вас достаточно информации и, конечно, ваша помощь будет высоко оценена!:) Береги себя.

1 ответ

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

Во-первых, теоретически, если ваши касательные и битангенты вычислены правильно, то вы должны получить точно такой же результат освещения при выполнении вычисления в касательном пространстве с нормальным касательного пространства [0,0,1].

Во-вторых, хотя общеизвестно, что вы должны преобразовать свои нормали из модели в пространство камеры путем умножения на матрицу обратного транспонирования модели-представления, как объяснено в этом учебном пособии, я обнаружил, что проблему с неправильным преобразованием освещения можно решить, если преобразовать нормаль касательная к матрице вида модели, а не к обратной модели транспонирования модели. Т.е. использовать normalMatrix = mat3(W); вместо normalMatrix = transpose(inverse(mat3(W)));,

В моем случае это "устранило" проблемы со светом, но я не знаю, почему это исправило его, но я не даю гарантии, что оно не (фактически я предполагаю, что это так) создает другие проблемы с затенением

Другие вопросы по тегам