Рендеринг с использованием карт нормалей вызывает ротационное освещение - PullRequest
0 голосов
/ 25 декабря 2018

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

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

Вот где рассчитывается TBN:

void calculateTBN()
{
    //get the normal matrix
    mat3 model = mat3(transpose(inverse(mat3(transform))));
    vec3 T = normalize(vec3(model * tangent.xyz ));

    vec3 N = normalize(vec3(model * normal      ));

    vec3 B = cross(N, T);

    mat3 TBN = mat3( T , B , N);

    outputVertex.TBN =TBN;
}

И нормаль отбирается и преобразуется:

vec3 calculateNormal()
{
    //Sort the input so that the normal is between 1 and minus 1 instead of 0 and 1
    vec3 input = texture2D(normalMap, inputFragment.textureCoord).xyz;

    input = 2.0 * input - vec3(1.0, 1.0, 1.0);

    vec3 newNormal = normalize(inputFragment.TBN* input);

    return newNormal;
}  

Мое освещение находится в мировом пространстве (насколько я понимаю, термин учитывает матрицу преобразования, но не матрицу камеры или проекции)

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

Если вы хотите увидеть какой-либо код освещения и т. Д., Я добавлю его, но я не думал, что это необходимо, поскольку он работает отдельно от этого.

РЕДАКТИРОВАТЬ:: По запросу, вот вершина и часть фрагмента шейдера

    #version 330


uniform mat4 T; // Translation matrix
uniform mat4 S; // Scale matrix
uniform mat4 R; // Rotation matrix
uniform mat4 camera; // camera matrix
uniform vec4 posRelParent; // the position relative to the parent


// Input vertex packet
layout (location = 0) in vec4 position;
layout (location = 2) in vec3 normal;
layout (location = 3) in vec4 tangent;
layout (location = 4) in vec4 bitangent;
layout (location = 8) in vec2 textureCoord;


// Output vertex packet
out packet {

    vec2 textureCoord;
    vec3 normal;
    vec3 vert;
    mat3 TBN;
    vec3 tangent;
    vec3 bitangent;
    vec3 normalTBN;

} outputVertex;

mat4 transform;
mat3 TBN;

void calculateTBN()
{
    //get the model matrix, the transform of the object with scaling and transform  removeds
    mat3 model = mat3(transpose(inverse(transform)));

    vec3 T = normalize(model*tangent.xyz);

    vec3 N = normalize(model*normal);

    //I used to retrieve the bitangents by crossing the normal and tangent but now they are calculated independently
    vec3 B = normalize(model*bitangent.xyz);

    TBN = mat3( T , B , N);

    outputVertex.TBN = TBN;

    //Pass though TBN vectors for colour debugging in the fragment shader
    outputVertex.tangent = T;
    outputVertex.bitangent = B;
    outputVertex.normalTBN = N;


}

void main(void) {
    outputVertex.textureCoord = textureCoord;


    // Setup local variable pos in case we want to modify it (since position is constant)
    vec4 pos = vec4(position.x, position.y, position.z, 1.0) + posRelParent;

    //Work out the transform matrix
    transform = T * R * S;

//Work out the normal for lighting
    mat3 normalMat = transpose(inverse(mat3(transform)));

    outputVertex.normal = normalize(normalMat* normal);

    calculateTBN();

    outputVertex.vert =(transform* pos).xyz;

    //Work out the final pos of the vertex
    gl_Position = camera * transform * pos;
    }

И Вектор освещения фрагмента:

vec3 applyLight(Light thisLight, vec3 baseColor, vec3 surfacePos, vec3 surfaceToCamera)
{
    float attenuation = 1.0f;
    vec3 lightPos = (thisLight.finalLightMatrix*thisLight.position).xyz;
    vec3 surfaceToLight;

    vec3 coneDir = normalize(thisLight.coneDirection);

    if (thisLight.position.w == 0.0f)
    {
        //Directional Light (all rays same angle, use position as direction)
        surfaceToLight = normalize( (thisLight.position).xyz);
        attenuation = 1.0f;
    }
    else
    {
        //Point light
        surfaceToLight = normalize(lightPos - surfacePos);


        float distanceToLight = length(lightPos - surfacePos);
        attenuation = 1.0 / (1.0f + thisLight.attenuation * pow(distanceToLight, 2));

        //Work out the Cone restrictions
        float lightToSurfaceAngle = degrees(acos(dot(-surfaceToLight, normalize(coneDir))));

        if (lightToSurfaceAngle > thisLight.coneAngle)
        {
            attenuation = 0.0;
        }
    }

}

Вот также основной фрагмент шейдера фрагмента:

void main(void) {
    //get the base colour from the texture
    vec4 tempFragColor = texture2D(textureImage, inputFragment.textureCoord).rgba;

    //Support for objects with and without a normal map
    if (useNormalMap == 1)
    {  
        calcedNormal = calculateNormal();
    }
    else
    {
        calcedNormal = inputFragment.normal;

    }


    vec3 surfaceToCamera = normalize((cameraPos_World) - (inputFragment.vert));

    vec3 tempColour = vec3(0.0, 0.0, 0.0);

    for (int count = 0; count < numLights; count++)
    {
        tempColour += applyLight(allLights[count], tempFragColor.xyz, inputFragment.vert, surfaceToCamera);
    }


    vec3 gamma = vec3(1.0 / 2.2);

    fragmentColour = vec4(pow(tempColour,gamma), tempFragColor.a);
    //fragmentColour = vec4(calcedNormal, 1);
}

Редактировать 2:

Геометрический шейдер, используемый для визуализации «отобранных» нормалей с помощью матрицы TBN, как показано здесь:

void GenerateLineAtVertex(int index)
{
    vec3 testSampledNormal = vec3(0, 0, 1);

    vec3 bitangent = cross(gs_in[index].normal, gs_in[index].tangent);

    mat3 TBN = mat3(gs_in[index].tangent, bitangent, gs_in[index].normal);

    testSampledNormal = TBN * testSampledNormal;


    gl_Position = gl_in[index].gl_Position;

    EmitVertex();

    gl_Position =
         gl_in[index].gl_Position 
        +  vec4(testSampledNormal, 0.0) * MAGNITUDE;


    EmitVertex();


    EndPrimitive();
}

И это вершинный шейдер

void main(void) {

// Setup local variable pos in case we want to modify it (since position is constant)
vec4 pos = vec4(position.x, position.y, position.z, 1.0);

mat4 transform = T* R * S;

// Apply transformation to pos and store result in gl_Position
gl_Position = projection* camera* transform * pos;


mat3 normalMatrix = mat3(transpose(inverse(camera * transform)));
vs_out.tangent = normalize(vec3(projection * vec4(normalMatrix * tangent.xyz, 0.0)));
vs_out.normal =  normalize(vec3(projection * vec4(normalMatrix * normal     , 0.0)));
}

Здесь визуализируются векторы TBN.Небольшие углы на точках связаны с тем, как я применяю матрицу проекции, а не с ошибками в фактических векторах.Красные линии просто показывают, где находятся стрелки, которые я нарисовал на текстуре, они не очень ясны под этим углом, вот и все.

TBN vectors

1 Ответ

0 голосов
/ 08 января 2019

Проблема решена!На самом деле это никак не связано с приведенным выше кодом, хотя спасибо всем, кто помог.

Я импортировал текстуру, используя свой собственный загрузчик текстур, который по умолчанию использует не гамма-скорректированный цвет SRGB в 32-битном цвете.Я переключил его на 24-битный и просто RGB цвет, и он сразу заработал.Типичные проблемы разработчика ....

Before and After

...