Нормальное отображение пошло ужасно неправильно

Я пытался реализовать нормальное отображение в своем приложении opengl, но не могу заставить его работать.

Это диффузная карта (к которой я добавляю коричневый цвет), и это карта нормалей.

Чтобы получить тангенциальные и битангентные (в других местах, называемых бинормалями?) Векторы, я запускаю эту функцию для каждого треугольника в моей сетке:

void getTangent(const glm::vec3 &v0, const glm::vec3 &v1, const glm::vec3 &v2,
const glm::vec2 &uv0, const glm::vec2 &uv1, const glm::vec2 &uv2,
std::vector<glm::vec3> &vTangents, std::vector<glm::vec3> &vBiangents)
{
    // Edges of the triangle : postion delta
    glm::vec3 deltaPos1 = v1-v0;
    glm::vec3 deltaPos2 = v2-v0;

    // UV delta
    glm::vec2 deltaUV1 = uv1-uv0;
    glm::vec2 deltaUV2 = uv2-uv0;

    float r = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);
    glm::vec3 tangent = (deltaPos1 * deltaUV2.y   - deltaPos2 * deltaUV1.y)*r;
    glm::vec3 bitangent = (deltaPos2 * deltaUV1.x   - deltaPos1 * deltaUV2.x)*r;

    for(int i = 0; i < 3; i++) {
        vTangents.push_back(tangent);
        vBiangents.push_back(bitangent);
    }
}

После этого я вызываю glBufferData для загрузки вершин, нормалей, uvs, касательных и битангентов в GPU. Вершинный шейдер:

#version 430

uniform mat4 ProjectionMatrix;
uniform mat4 CameraMatrix;
uniform mat4 ModelMatrix;

in vec3 vertex;
in vec3 normal;
in vec2 uv;
in vec3 tangent;
in vec3 bitangent;

out vec2 fsCoords;
out vec3 fsVertex;
out mat3 TBNMatrix;

void main()
{
    gl_Position = ProjectionMatrix * CameraMatrix * ModelMatrix * vec4(vertex, 1.0);

    fsCoords = uv;
    fsVertex = vertex;

    TBNMatrix = mat3(tangent, bitangent, normal);
}

Фрагмент шейдера:

#version 430

uniform sampler2D diffuseMap;
uniform sampler2D normalMap;
uniform mat4 ModelMatrix;
uniform vec3 CameraPosition;

uniform struct Light {
    float ambient;
    vec3 position;
} light;
uniform float shininess;

in vec2 fsCoords;
in vec3 fsVertex;
in mat3 TBNMatrix;

out vec4 color;

void main()
{
    //base color
    const vec3 brownColor = vec3(153.0 / 255.0, 102.0 / 255.0, 51.0 / 255.0);
    color = vec4(brownColor * (texture(diffuseMap, fsCoords).rgb + 0.25), 1.0);//add a fixed base color (0.25), because its dark as hell

    //general vars
    vec3 normal = texture(normalMap, fsCoords).rgb * 2.0 - 1.0;
    vec3 surfacePos = vec3(ModelMatrix * vec4(fsVertex, 1.0));
    vec3 surfaceToLight = normalize(TBNMatrix * (light.position - surfacePos)); //unit vector
    vec3 eyePos = TBNMatrix * CameraPosition;

    //diffuse
    float diffuse = max(0.0, dot(normal, surfaceToLight));

    //specular
    float specular;
    vec3 incidentVector = -surfaceToLight; //unit
    vec3 reflectionVector = reflect(incidentVector, normal); //unit vector
    vec3 surfaceToCamera = normalize(eyePos - surfacePos); //unit vector
    float cosAngle = max(0.0, dot(surfaceToCamera, reflectionVector));
    if(diffuse > 0.0)
        specular = pow(cosAngle, shininess);

    //add lighting to the fragment color (no attenuation for now)
    color.rgb *= light.ambient;
    color.rgb += diffuse + specular;
}

Изображение, которое я получаю, совершенно неверно. (свет установлен на камеру)ужасные результаты

Что я здесь не так делаю?

2 ответа

Моя ставка на настройку цвета / смешивание во фрагментном шейдере...

  1. Вы устанавливаете выходной цвет более одного раза

    Если я правильно помню, на некоторых драйверах GFX, которые делают большие проблемы, например, все после линии

    color = vec4(brownColor * (texture(diffuseMap, fsCoords).rgb + 0.25), 1.0);//add a fixed base color (0.25), because its dark as hell
    

    может быть удален драйвером...

  2. вы добавляете color а также intensities вместо color*intensity

    но я мог не заметить чего-то.

  3. попробуй просто нормальный / рельефный сначала

    Не обращайте внимания на эмбиент, рефлекс, зеркальность... и затем, если это сработает, добавьте остальные по одному Всегда проверяйте журналы компиляции шейдера

Лень для дальнейшего анализа вашего кода, вот как я это делаю:

пример рельефного отображения

Размер слева - это объект космического корабля (похожий на ZXS Elite's Viper) с фиксированной функцией. Правая сторона такая же (немного другое вращение объекта) с GLSL-шейдерами на месте и этой картой нормалей / ударов

[Вершина]

//------------------------------------------------------------------
#version 420 core
//------------------------------------------------------------------
// texture units:
// 0 - texture0 map 2D rgba
// 1 - texture1 map 2D rgba
// 2 - normal map 2D xyz
// 3 - specular map 2D i
// 4 - light map 2D rgb rgb
// 5 - enviroment/skybox cube map 3D rgb

uniform mat4x4 tm_l2g;
uniform mat4x4 tm_l2g_dir;
uniform mat4x4 tm_g2s;

uniform mat4x4 tm_l2s_per;
uniform mat4x4 tm_per;

layout(location=0) in vec3 pos;
layout(location=1) in vec4 col;
layout(location=2) in vec2 txr;
layout(location=3) in vec3 tan;
layout(location=4) in vec3 bin;
layout(location=5) in vec3 nor;

out smooth vec3 pixel_pos;
out smooth vec4 pixel_col;
out smooth vec2 pixel_txr;
//out flat   mat3 pixel_TBN;
out smooth mat3 pixel_TBN;
//------------------------------------------------------------------
void main(void)
    {
    vec4 p;
    p.xyz=pos;
    p.w=1.0;
    p=tm_l2g*p;
    pixel_pos=p.xyz;
    p=tm_g2s*p;
    gl_Position=p;
    pixel_col=col;
    pixel_txr=txr;
    p.xyz=tan.xyz; p.w=1.0; pixel_TBN[0]=normalize((tm_l2g_dir*p).xyz);
    p.xyz=bin.xyz; p.w=1.0; pixel_TBN[1]=normalize((tm_l2g_dir*p).xyz);
    p.xyz=nor.xyz; p.w=1.0; pixel_TBN[2]=normalize((tm_l2g_dir*p).xyz);
    }
//------------------------------------------------------------------

[Фрагмент]

//------------------------------------------------------------------
#version 420 core
//------------------------------------------------------------------
in smooth vec3 pixel_pos;
in smooth vec4 pixel_col;
in smooth vec2 pixel_txr;
//in flat   mat3 pixel_TBN;
in smooth mat3 pixel_TBN;

uniform sampler2D   txr_texture0;
uniform sampler2D   txr_texture1;
uniform sampler2D   txr_normal;
uniform sampler2D   txr_specular;
uniform sampler2D   txr_light;
uniform samplerCube txr_skybox;

const int _lights=3;
uniform vec3 light_col0=vec3(0.1,0.1,0.1);
uniform vec3 light_dir[_lights]=         // direction to local star in ellipsoid space
    {
    vec3(0.0,0.0,+1.0),
    vec3(0.0,0.0,+1.0),
    vec3(0.0,0.0,+1.0),
    };
uniform vec3 light_col[_lights]=         // local star color * visual intensity
    {
    vec3(1.0,0.0,0.0),
    vec3(0.0,1.0,0.0),
    vec3(0.0,0.0,1.0),
    };

out layout(location=0) vec4 frag_col;

const vec4 v05=vec4(0.5,0.5,0.5,0.5);
const bool _blend=false;
const bool _reflect=true;
//------------------------------------------------------------------
void main(void)
    {
    float a=0.0,b,li;
    vec4 col,blend0,blend1,specul,skybox;
    vec3 normal;
    col=(texture2D(txr_normal,pixel_txr.st)-v05)*2.0;       // normal/bump maping
//  normal=pixel_TBN*col.xyz;
    normal=pixel_TBN[0];
    blend0=texture(txr_texture0,pixel_txr.st);
    blend1=texture(txr_texture1,pixel_txr.st);
    specul=texture(txr_specular,pixel_txr.st);
    skybox=texture(txr_skybox,normal);

    if (_blend)
        {
        a=blend1.a;
        blend0*=1.0-a;
        blend1*=a;
        blend0+=blend1;
        blend0.a=a;
        }

    col.xyz=light_col0; col.a=0.0; li=0.0;                          // normal shading (aj s bump mapingom)
    for (int i=0;i<_lights;i++)
            {
            b=dot(light_dir[i],normal.xyz);
            if (b<0.0) b=0.0;
//          b*=specul.r;
            li+=b;
            col.xyz+=light_col[i]*b;
            }
    col*=blend0;
    if (li<=0.1)
        {
        blend0=texture2D(txr_light,pixel_txr.st);
        blend0*=1.0-a;
        blend0.a=a;
        col+=blend0;
        }
    if (_reflect) col+=skybox*specul.r;
        col*=pixel_col;
    if (col.r<0.0) col.r=0.0;
    if (col.g<0.0) col.g=0.0;
    if (col.b<0.0) col.b=0.0;
    a=0.0;
    if (a<col.r) a=col.r;
    if (a<col.g) a=col.g;
    if (a<col.b) a=col.b;
    if (a>1.0)
        {
        a=1.0/a;
        col.r*=a;
        col.g*=a;
        col.b*=a;
        }
    frag_col=col;
    }
//------------------------------------------------------------------

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

Так что извлекайте из него только то, что вам нужно. Если вы перепутали с именами переменных, прокомментируйте меня...

  • tm_ обозначает матрицу преобразования
  • l2g обозначает местную систему координат в преобразование глобальной системы координат
  • dir означает, что трансформация меняет только направление (смещение 0,0,0)
  • g2s обозначает глобальный экран...
  • per это перспективное преобразование...

Журнал компиляции GLSL

Вы должны получить его содержимое программно после компиляции вашего шейдера (не приложения!!!). Я делаю это с вызовом функции glGetShaderInfoLog для каждого шейдера программа, которую я использую...

[Заметки]

Некоторые драйверы оптимизируют "неиспользуемые" переменные. Как вы можете видеть на изображении txr_texture1 не найден, даже если фрагментный шейдер содержит его в коде, но смешивание не используется в этом приложении, поэтому драйвер удалил его самостоятельно...

Журналы шейдеров могут многое показать (ошибки синтаксиса, предупреждения...)

Есть несколько GLSL IDE для упрощения работы с шейдерами, но я предпочитаю свою собственную, потому что я могу напрямую использовать в ней код целевого приложения. Моя выглядит так:

GLSL IDE

каждое текстовое окно является источником шейдера (вершина, фрагмент,...), правая нижняя часть - буфер обмена, левая верхняя часть - журнал шейдера после последней компиляции, а левая нижняя часть - предварительный просмотр. Мне удалось закодировать его как IDE в стиле Borland (с ключами и подсветкой синтаксиса), другие IDE, которые я видел, выглядят похожими (разные цвета грубой:)) в любом случае, если вы хотите поиграть с загрузкой шейдера в такое приложение или сделать это самостоятельно очень поможет...

Также может быть проблема с созданием TBN

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

нормативы

Я постараюсь, чтобы ваш код работал. Вы пробовали это с движущейся камерой?

Я нигде не вижу, чтобы вы преобразовали TBNMatrix с матрицами преобразования, просмотра и моделирования. Вы пробовали с vec3 normal = TBNMatrix[2]; оригинальные нормали? (Фрагмент шейдера)

Следующее может помочь. В шейдере Vertex у вас есть:

uniform mat4 ProjectionMatrix;
uniform mat4 CameraMatrix;
uniform mat4 ModelMatrix;

Однако здесь должны использоваться только эти 3 матрицы:

uniform mat4 PCM;
uniform mat4 MIT;         //could be mat3
uniform mat4 ModelMatrix; //could be mat3

Более эффективно вычислять произведение этих матриц на CPU (и дает то же самое, потому что умножение матриц является ассоциативным). Затем этот продукт PCM можно использовать как для вычисления новой позиции с одним умножением на вершину:

gl_Position = PCM * vec4(vertex, 1.0);

MIT это обратная транспонирование ModelMatrix, вы должны рассчитать его на процессоре. Это можно использовать для преобразования нормалей:

vec4 tang = ModelMatrix*vec4(tangent,0);
vec4 bita= ModelMatrix*vec4(bitangent,0);
vec4 norm= PCMIT*vec4(tangent,0);   
TBNMatrix = mat3(normalize(tang.xyz), normalize(bita.xyz), normalize(normal.xyz));

Я не уверен, что происходит с тангенсом и битангенсом, но таким образом нормаль будет оставаться перпендикулярной им. Это легко доказать. Здесь я использую ° b как скалярное произведение векторов a и b. Итак, пусть n - некоторая нормаль, а a - некоторый вектор на поверхности (например, касательная {bi}, ребро треугольника), и пусть A - любое преобразование. Затем:

0 = an = A ^ (- 1) A a ° n = A a ° A ^ (- T) n = 0

Где я использовал равенство A x ° y = x ° A^T y. Следовательно, если a перпендикулярно n, то A a перпендикулярно A^(-T) n, поэтому мы должны преобразовать его с помощью обратной транспонирования матрицы. Однако нормаль должна иметь длину 1, поэтому после преобразований ее следует нормализовать.

Вы также можете получить нормаль перпендикуляра, выполнив это:

vec3 normal = normalize(cross(tangent, bitangent));

Где крест (a,b) является функцией, которая вычисляет перекрестное произведение a и b, ведьма всегда перпендикулярна как a, так и b.

Извините за мой английский:)

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