使用 Assimp 和 OpenGL 渲染问题

Render Issues Using Assimp and OpenGL

我正在使用 Assimp 加载模型以在 OpenGL 中渲染,但 运行 遇到了 chunks/pieces 网格不渲染的问题。

示例:

什么样的模型应该看起来像:

我最终渲染的是:

如您所见,部分模型渲染正确,但并非全部。

我已经多次验证从 assimp 加载的网格正在将正确的顶点和索引加载到我的“网格”中 class。这是我加载模型的代码:

此函数将递归地为所有子节点调用自身并加载节点内的每个网格。然后通过创建顶点和面的向量,每个网格将被转换成我自己的“网格”class。

void Model::LoadAssimpNode(aiNode* node, const aiScene* scene)
{
    // Process assimp meshes
    for (unsigned int i = 0; i < node->mNumMeshes; i++)
    {
        aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
        this->meshes.push_back(this->LoadAssimpMesh(mesh, scene));
    }

    // Recursivley processes child nodes
    for (unsigned int i = 0; i < node->mNumChildren; i++)
    {
        this->LoadAssimpNode(node->mChildren[i], scene);
    }
}

Mesh Model::LoadAssimpMesh(aiMesh* mesh, const aiScene* scene)
{
    std::vector<sVertex> vertices;
    for (unsigned int i = 0; i < mesh->mNumVertices; i++)
    {
        sVertex vertex;

        vertex.x = mesh->mVertices[i].x;
        vertex.y = mesh->mVertices[i].y;
        vertex.z = mesh->mVertices[i].z;

        vertex.nx = mesh->mNormals[i].x;
        vertex.ny = mesh->mNormals[i].y;
        vertex.nz = mesh->mNormals[i].z;

        if (mesh->mTextureCoords[0])
        {
            vertex.u0 = mesh->mTextureCoords[0][i].x;
            vertex.v0 = mesh->mTextureCoords[0][i].y;
        }

        vertices.push_back(vertex);
    }

    std::vector<sTriangle> faces;
    for (unsigned int i = 0; i < mesh->mNumFaces; i++)
    {
        sTriangle face;
        aiFace assimpFace = mesh->mFaces[i];
        if (assimpFace.mNumIndices != 3)
        {
            std::cout << "Face is not a triangle!" << std::endl;
        }

        for (unsigned int j = 0; j < assimpFace.mNumIndices; j++)
        {
            face.vertIndex[j] = assimpFace.mIndices[j];
        }

        faces.push_back(face);
    }

    std::vector<Texture> textures;
    if (mesh->mMaterialIndex >= 0)
    {
        aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];

        // Sampler names should adhere to the following convention:
        // Diffuse: texure_diffuseN
        // Specular: texture_specularN
        // Normal: texture_normalN
        // Where N = texture numbers

        for (Texture texture : this->LoadAssimpMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse"))
        {
            this->loadedTextures.insert(std::make_pair(texture.path.C_Str(), texture));
            textures.push_back(texture);
        }

        for (Texture texture : this->LoadAssimpMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular"))
        {
            this->loadedTextures.insert(std::make_pair(texture.path.C_Str(), texture));
            textures.push_back(texture);
        }
    }

    return Mesh(vertices, faces, textures);
}

sVertex 和 sTriangle 结构定义为:

struct sVertex
{
    float x, y, z; 
    float nx, ny, nz;
    float u0, v0;
};

struct sTriangle
{
    unsigned int vertIndex[3];
};

现在模型已从 assimp 有效加载,我们现在调用 SetupMesh() 函数来设置网格各自的 VAO、VBO 和 EBO:

void Mesh::SetupMesh()
{
    // Generate IDs for our VAO, VBO and EBO
    glGenVertexArrays(1, &this->VAO);
    glGenBuffers(1, &this->VBO);
    glGenBuffers(1, &this->EBO);

    glBindVertexArray(this->VAO);

    // Now ANY state that is related to vertex or index buffer
    //  and vertex attribute layout, is stored in the 'state' 
    //  of the VAO... 

    // Tell open GL where to look for for vertex data
    glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
    glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(sVertex), &this->vertices[0], GL_STATIC_DRAW);

    // Tell open GL where our index buffer begins (AKA: where to look for faces)
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->faces.size() * sizeof(sTriangle), &this->faces[0], GL_STATIC_DRAW);

    // Set the vertex attributes for this shader
    // Layout information can be found in the vertex shader, currently:
    // 0 = position
    // 1 = normals
    // 2 = texture coordinates
    glEnableVertexAttribArray(0);       // position
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(sVertex), (void*) offsetof(sVertex, x));

    glEnableVertexAttribArray(1);       // normal
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(sVertex), (void*) offsetof(sVertex, nx));

    glEnableVertexAttribArray(2);       // textureCoordinates
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(sVertex), (void*)offsetof(sVertex, u0));

    // Now that all the parts are set up, unbind buffers
    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);
}

完成所有设置后,我现在将为每个网格调用 Draw 方法以在我的渲染循环中渲染:

void Mesh::Draw(const CompiledShader& shader)
{
    glm::mat4 matModel = glm::mat4(1.0f);
    glm::mat4 matTranslate = glm::translate(glm::mat4(1.0f), this->positionXYZ); // Translation matrix
    glm::mat4 rotateX = glm::rotate(glm::mat4(1.0f), this->orientationXYZ.x, glm::vec3(1.0f, 0.0f, 0.0f)); // X axis rotation
    glm::mat4 rotateY = glm::rotate(glm::mat4(1.0f), this->orientationXYZ.y, glm::vec3(0.0f, 1.0f, 0.0f)); // Y axis rotation
    glm::mat4 rotateZ = glm::rotate(glm::mat4(1.0f), this->orientationXYZ.z, glm::vec3(0.0f, 0.0f, 1.0f)); // Z axis rotation
    glm::mat4 matScale = glm::scale(glm::mat4(1.0f), glm::vec3(this->scale, this->scale, this->scale)); // Scale the mesh
    glm::mat4 matInvTransposeModel = glm::inverse(glm::transpose(matModel));

    // Apply all the transformations to our matrix
    matModel = matModel * matTranslate;
    matModel = matModel * rotateZ;
    matModel = matModel * rotateY;
    matModel = matModel * rotateX;
    matModel = matModel * matScale;

    glUseProgram(shader.ID);

    glUniformMatrix4fv(glGetUniformLocation(shader.ID, "matModel"), 1, GL_FALSE, glm::value_ptr(matModel)); // Tell shader the model matrix (AKA: Position orientation and scale)
    glUniformMatrix4fv(glGetUniformLocation(shader.ID, "matModelInverseTranspose"), 1, GL_FALSE, glm::value_ptr(matInvTransposeModel));

    // Draw the mesh
    glBindVertexArray(this->VAO);
    glDrawElements(GL_TRIANGLES, this->faces.size(), GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);

}

我的着色器非常简单,其中像素的颜色等于顶点的法线:

顶点着色器:

#version 420

layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 textureCoordinates;

uniform mat4 matModel;
uniform mat4 matView;
uniform mat4 matProjection;
uniform mat4 matModelInverseTranspose;  // For normal calculation
    
out vec4 fVertWorldLocation;
out vec4 fNormal;
out vec2 TextureCoordinates;

void main()
{   
    mat4 MVP = matProjection * matView * matModel;
            
    gl_Position = MVP * vec4(position, 1.0f);   
    
    TextureCoordinates = textureCoordinates;
    
    // The location of the vertex in "world" space (not screen space)
    fVertWorldLocation = matModel * vec4(position, 1.0f);
    
    // Calculate the normal based on any rotation we've applied.
    // This inverse transpose removes scaling and tranlation (movement) 
    //  from the matrix.
    fNormal = matModelInverseTranspose * vec4(normal, 1.0f);
    
    
};

片段着色器:

#version 420

in vec2 TextureCoordinates;
in vec4 fNormal;

out vec4 Color;

uniform sampler2D texture_diffuse;

void main()
{
    //Color = vec4(texture(texture_diffuse, TextureCoordinates));
    //Color = vec4(TextureCoordinates, 1.0f, 1.0f);
    Color = fNormal;
    
}

很抱歉这篇 post 的篇幅太长了,但我觉得所有这些都是让我的观点得到理解所必需的。

如果有人能指出我在这里做错了什么,将不胜感激!我觉得我在这里需要多一双眼睛,因为我已经无数次阅读我的代码,但似乎什么也想不出来。

犯了一个愚蠢的错误,我的印象是 glDrawElements() 函数中的“计数”参数想要面数而不是索引数。

问题已通过更改我的 glDrawElements 调用解决:

   glDrawElements(GL_TRIANGLES, this->faces.size(), GL_UNSIGNED_INT, 0);

为此:

  glDrawElements(GL_TRIANGLES, this->faces.size() * 3, GL_UNSIGNED_INT, 0);