c++ - 从 Assimp 加载 Collada (dae) 模型显示法线不正确

标签 c++ glm-math collada assimp

解决方案:

感谢 Rabbid76,我需要使用模型矩阵更新顶点着色器中的 Normal vector 。更新了顶点着色器:

#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 中正确加载 collada (dae) 文件,但法线似乎出现错误。我希望得到帮助来解决这个问题。我有一种感觉,这与我处理变换矩阵的方式有关。作为示例,下面是 OpenGL 应用程序加载 obj 文件的屏幕截图:

enter image description here

在上面的屏幕截图中,灯光直接位于模型上方 x=0 和 z=0 处。法线显示正确。当我加载 dae 文件时,我得到以下信息:

enter image description here

光线位置似乎来自-z 侧。

这是我当前必须加载模型的代码:

  1. 加载模型文件,并调用包含 aiMatrix4x4()processNode() 方法
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() 处理收集所有网格数据(顶点、索引等)
  • 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);
    }
    

    最佳答案

    FragPos 是世界空间中的位置,因为它是模型矩阵变换后的顶点位置。 lightPosviewPos 似乎也是世界空间中的位置。
    因此也必须将法线 vector aNormal从模型空间转换到世界空间。

    您必须通过 4*4 模型矩阵左上角 3*3 的转置来变换法线 vector :

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

    可能通过 4*4 模型矩阵的左上角 3*3 进行变换就足够了:
    (参见In which cases is the inverse matrix equal to the transpose?)

    Normal = mat3(model) * aNormal;
    

    <子> 另请参阅:
    Why is the transposed inverse of the model view matrix used to transform the normal vectors?
    Why transforming normals with the transpose of the inverse of the modelview matrix?

    关于c++ - 从 Assimp 加载 Collada (dae) 模型显示法线不正确,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59833642/

    相关文章:

    c++ - 在字符串中查找单词或短语的实例

    C++重新定义现有类的输出流

    c++ - OpenGL 矩阵和着色器混淆

    c++ - OpenCL对齐问题

    c++ - 改变 glm::perspective 中的角度反射(reflect)了对象

    swift - xcode9/SceneKit - .dae 文件未加载到 SCNScene - 返回 nil

    c++ - 使用 DirectShow 捕获帧并使用 OpenCV 进行处理

    c++ - gdb "watch pointer "提供太多硬件观察点错误

    C++ OpenGL 错误的 Collada 纹理坐标

    javascript - ThreeJS collada 文件未居中