Загрузка модели Collada (dae) из Assimp показывает некорректные нормали - PullRequest
0 голосов
/ 21 января 2020

РЕШЕНИЕ:

Благодаря Rabbid76 мне нужно было обновить вектор Normal в вершинном шейдере с помощью матрицы модели. Обновленный вершинный шейдер:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;

out vec3 FragPos;
out vec3 Normal;

uniform mat4 projection; 
uniform mat4 view; 
uniform mat4 model;

uniform float scale;

void main()
{
    FragPos = vec3(model * vec4(aPos, 1.0));
    Normal = vec3(model * vec4(aNormal, 0.0));
    gl_Position = projection * view * vec4(FragPos, 1.0);
}

enter image description here

ВОПРОС

Пытаюсь правильно загрузить колладу ( Да) файл в Assimp, но нормальные, кажется, выходит неправильно. Я хотел бы помочь с выяснением этого. У меня есть ощущение, что это связано с тем, как я обращаюсь с матрицей преобразования. В качестве примера приведен скриншот приложения OpenGL, загружающего файл obj:

enter image description here

На приведенном выше снимке экрана источник света расположен прямо над моделями. при x = 0 и z = 0. Нормалы отображаются правильно. Когда я загружаю файл dae, я получаю следующее:

enter image description here

Светлая позиция, кажется, идет со стороны -z.

вот код, который мне нужен для загрузки моделей:

  1. Загрузите файл модели и вызовите метод processNode(), который включает aiMatrix4x4()
void Model::loadModel(std::string filename)
{
    Assimp::Importer importer;
    const aiScene *scene = importer.ReadFile(filename, aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace | aiProcess_GenBoundingBoxes);

    if (!scene || !scene->mRootNode) {
        std::cout << "ERROR::ASSIMP Could not load model: " << importer.GetErrorString() << std::endl;
    }
    else {
        this->directory = filename.substr(0, filename.find_last_of('/'));

        this->processNode(scene->mRootNode, scene, aiMatrix4x4());
    }
}

processNode() - это рекурсивный метод, который в основном перебирает node->mMeshes, умножая преобразование.
void Model::processNode(aiNode* node, const aiScene* scene, aiMatrix4x4 transformation)
{ 
    for (unsigned int i = 0; i < node->mNumMeshes; i++) {
        aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];

        // only apply transformation on meshs not entities such as lights or camera.
        transformation *= node->mTransformation;

        this->meshes.push_back(processMesh(mesh, scene, transformation));
    }

    for (unsigned int i = 0; i < node->mNumChildren; i++)
    {
        processNode(node->mChildren[i], scene, transformation);
    }
}
processMesh() обрабатывает сбор всех данных sh (вершины, индексы и т. Д. c)
Mesh Model::processMesh(aiMesh* mesh, const aiScene* scene, aiMatrix4x4 transformation)
{
    glm::vec3 extents;
    glm::vec3 origin;

    std::vector<Vertex> vertices = this->vertices(mesh, extents, origin, transformation);
    std::vector<unsigned int> indices = this->indices(mesh);
    std::vector<Texture> textures = this->textures(mesh, scene);

    return Mesh(
        vertices,
        indices,
        textures,
        extents,
        origin,
        mesh->mName
    );
}
Затем вызывается метод vertices(), чтобы получить все вершины. Он проходит матрицу преобразования. Здесь я умножаю вершины на матрицу (transformation * mesh->mVertices[i];). У меня сильное чувство, что я не делаю что-то прямо здесь, и что-то упускаю.
std::vector<Vertex> Model::vertices(aiMesh* mesh, glm::vec3& extents, glm::vec3 &origin, aiMatrix4x4 transformation)
{
    std::vector<Vertex> vertices;

    for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
        Vertex vertex;

        glm::vec3 vector3;

        aiVector3D v = transformation * mesh->mVertices[i];

        // Vertices
        vector3.x = v.x;
        vector3.y = v.y;
        vector3.z = v.z;

        vertex.position = vector3;

        // Normals
        if (mesh->mNormals) {
            vector3.x = mesh->mNormals[i].x;
            vector3.y = mesh->mNormals[i].y;
            vector3.z = mesh->mNormals[i].z;
            vertex.normal = vector3;
        }


        // Texture coordinates
        if (mesh->mTextureCoords[0]) {
            glm::vec2 vector2;

            vector2.x = mesh->mTextureCoords[0][i].x;
            vector2.y = mesh->mTextureCoords[0][i].y;
            vertex.texCoord = vector2;
        }
        else {
            vertex.texCoord = glm::vec2(0, 0);
        }

        if (mesh->mTangents) {
            vector3.x = mesh->mTangents[i].x;
            vector3.y = mesh->mTangents[i].y;
            vector3.z = mesh->mTangents[i].z;
            vertex.tangent = vector3;
        }

        // Bitangent
        if (mesh->mBitangents) {
            vector3.x = mesh->mBitangents[i].x;
            vector3.y = mesh->mBitangents[i].y;
            vector3.z = mesh->mBitangents[i].z;
            vertex.bitangent = vector3;
        }


        vertices.push_back(vertex);
    }

    glm::vec3 min = glm::vec3(mesh->mAABB.mMin.x, mesh->mAABB.mMin.y, mesh->mAABB.mMin.z);
    glm::vec3 max = glm::vec3(mesh->mAABB.mMax.x, mesh->mAABB.mMax.y, mesh->mAABB.mMax.z);

    extents = (max - min) * 0.5f;
    origin = glm::vec3((min.x + max.x) / 2.0f, (min.y + max.y) / 2.0f, (min.z + max.z) / 2.0f);

    printf("%f,%f,%f\n", origin.x, origin.y, origin.z);

    return vertices;
}

В качестве дополнительной заметки, если это полезно, вот фрагментный шейдер, который я использую на модели:

#version 330 core
out vec4 FragColor;

in vec3 Normal;  
in vec3 FragPos;

uniform vec3 lightPos;
uniform vec3 viewPos;

vec3 lightColor = vec3(1,1,1);
vec3 objectColor = vec3(0.6, 0.6, 0.6);
uniform float shininess = 32.0f;
uniform vec3 material_specular = vec3(0.1f, 0.1f, 0.1f);
uniform vec3 light_specular = vec3(0.5f, 0.5f, 0.5f);

void main()
{
    // ambient
    float ambientStrength = 0.2;
    vec3 ambient = ambientStrength * lightColor;

    // diffuse 
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(lightPos - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = diff * lightColor;

    // specular
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);  
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), shininess);
    vec3 specular = light_specular * (spec * material_specular);  

    vec3 result = (ambient + diffuse + specular) * objectColor;
    FragColor = vec4(result, 1.0);
}

Вот вершинный шейдер:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;

out vec3 FragPos;
out vec3 Normal;

uniform mat4 projection; 
uniform mat4 view; 
uniform mat4 model;

uniform float scale;

void main()
{
    FragPos = vec3(model * vec4(aPos, 1.0));
    Normal = aNormal;  
    gl_Position = projection * view * vec4(FragPos, 1.0);
}

1 Ответ

1 голос
/ 21 января 2020

FragPos - это позиция в мировом пространстве, потому что это позиция вершины, преобразованная матрицей модели. lightPos и viewPos, похоже, тоже являются позициями в мировом пространстве.
Так что нужно преобразовать вектор нормали aNormal, из модельного пространства в мировое пространство тоже.

Вы должны преобразовать вектор нормали с помощью обратного транспонированного верхнего левого 3 * 3 матрицы модели 4 * 4:

Normal = transpose(inverse(mat3(model))) * aNormal;

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

Normal = mat3(model) * aNormal;

См. Также:
Почему транспонированная инверсия матрицы представления модели используется для преобразования векторов нормалей?
Почему преобразование нормалей с помощью транспонирования инверсии матрицы вида модели?

...