【问题标题】:Trouble loading and drawing a .obj model using Assimp and OpenGL使用 Assimp 和 OpenGL 加载和绘制 .obj 模型时遇到问题
【发布时间】:2016-12-28 10:02:02
【问题描述】:

在完成 OpenGL 的基础知识(创建窗口、制作 2D 三角形、着色器等)之后,我决定开始尝试加载简单的 .obj 模型。最推荐的库是Assimp,所以我跟着some tutorials 修改了我的项目以加载模型。但不幸的是,模型显示得很奇怪。我创建了以下代码来显示这一点:

#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>

struct Vertex
{
    glm::vec3 position;
    glm::vec3 normal;
};

struct Mesh
{
    //The vertex array object, vertex buffer object and element buffer object
    GLuint VAO;
    GLuint VBO;
    GLuint EBO;
    //Vectors for the vertices and indices to put in the buffers
    std::vector<Vertex> vertices;
    std::vector<GLuint> indices;

    //Constructor
    Mesh(const std::vector<Vertex>& vertices, const std::vector<GLuint>& indices)
    {
        this->vertices = vertices;
        this->indices  = indices;

        //Generate the VAO
        glGenVertexArrays(1, &VAO);

        //Generate the buffer objects
        glGenBuffers(1, &VBO);
        glGenBuffers(1, &EBO);

        //Bind the VAO
        glBindVertexArray(VAO);

        //Bind the VBO and set the vertices
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), &vertices.at(0), GL_STATIC_DRAW);

        //Enable the first attribute pointer
        glEnableVertexAttribArray(0);
        //Set the attribute pointer    The stride is meant to be 'sizeof(Vertex)', but it doesn't work at all that way
        //                                              \/
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

        //Enable the second attribute pointer
        glEnableVertexAttribArray(1);
        //Set the attribute pointer                   ditto
        //                                              \/
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*) offsetof(Vertex, normal));

        //Bind the EBO and set the indices
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indices.size(), &indices.at(0), GL_STATIC_DRAW);

        //Report any errors
        GLenum error = glGetError();
        if (error != GL_NO_ERROR)
        {
            std::cerr << "Error while creating mesh!" << std::endl;
        }

        glBindVertexArray(0);
    }

    void draw()
    {
        //Bind the VAO
        glBindVertexArray(VAO);

        //Bind the ELement Buffer Object
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);

        //Draw the mesh
        glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);

        //Unbind the VAO
        glBindVertexArray(0);
    }
};

int main()
{
    //Intialize GLFW (no error checking for brevity)
    glfwInit();

    //Set the OpenGL version to 3.3
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    //Create a new window
    GLFWwindow* window = glfwCreateWindow(800, 600, "Model Testing", NULL, NULL);

    glfwMakeContextCurrent(window);

    //Initialize glew (no checking again)
    glewInit();

    glViewport(0, 0, 800, 600);
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);

    //Load the model
    Assimp::Importer importer;
    const aiScene* scene = importer.ReadFile("mymodel.obj", aiProcess_Triangulate | aiProcess_GenNormals);

    //Check for errors
    if ((!scene) || (scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE) || (!scene->mRootNode))
    {
        std::cerr << "Error loading mymodel.obj: " << std::string(importer.GetErrorString()) << std::endl;
        //Return fail
        return -1;
    }

    //A vector to store the meshes
    std::vector<std::unique_ptr<Mesh> > meshes;
    //Iterate over the meshes
    for (unsigned int i = 0; i < scene->mNumMeshes; ++i)
    {
        //Get the mesh
        aiMesh* mesh = scene->mMeshes[i];

        //Create vectors for the vertices and indices
        std::vector<Vertex> vertices;
        std::vector<GLuint> indices;

        //Iterate over the vertices of the mesh
        for (unsigned int j = 0; j < mesh->mNumVertices; ++j)
        {
            //Create a vertex to store the mesh's vertices temporarily
            Vertex tempVertex;

            //Set the positions
            tempVertex.position.x = mesh->mVertices[j].x;
            tempVertex.position.y = mesh->mVertices[j].y;
            tempVertex.position.z = mesh->mVertices[j].z;

            //Set the normals
            tempVertex.normal.x   = mesh->mNormals[j].x;
            tempVertex.normal.y   = mesh->mNormals[j].y;
            tempVertex.normal.z   = mesh->mNormals[j].z;

            //Add the vertex to the vertices vector
            vertices.push_back(tempVertex);
        }

        //Iterate over the faces of the mesh
        for (unsigned int j = 0; j < mesh->mNumFaces; ++j)
        {
            //Get the face
            aiFace face = mesh->mFaces[j];
            //Add the indices of the face to the vector
            for (unsigned int k = 0; k < face.mNumIndices; ++k) {indices.push_back(face.mIndices[k]);}
        }

        //Create a new mesh and add it to the vector
        meshes.push_back(std::unique_ptr<Mesh>(new Mesh(std::move(vertices), std::move(indices))));
    }

    //While the window shouldn't be closed
    while (!glfwWindowShouldClose(window))
    {
        //Clear the buffer
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        //Draw all the meshes
        for (auto& mesh : meshes) {mesh.get()->draw();}

        //Swap the buffers
        glfwSwapBuffers(window);
    }

    //Close the window now that it's not needed anymore
    glfwDestroyWindow(window);

    return 0;
}

当程序加载this teapot 时,我的屏幕如下所示:

从更远的另一个角度(使用比上述更复杂的程序):

如果有用的话,我正在运行带有 Nvidia GTX 750 Ti 驱动程序版本 361.45 的 Ubuntu 16.04

【问题讨论】:

  • 步幅不应为零。你说它根本不起作用 - 如果你把步幅设置为sizeof(Vertex) 会发生什么?还有 Vertex 的大小是多少(sizeof(Vertex) 返回什么)?
  • glVertexAttribPointer(0, ... glGetAttribLocation 查询位置
  • @Sam 将步幅更改为sizeof(Vertex) 确实解决了它,我想我只是假设我过去在玩 OpenGL 时它不起作用。如果你愿意,你可以从中做出答案,我会接受的:)
  • 完成,谢谢! :)

标签: c++ opengl 3d assimp


【解决方案1】:

尝试将 ebo 的绑定移动到网格构造函数中的 vbo 之后,如下所示:

//Bind the VBO and set the vertices
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), &vertices.at(0), GL_STATIC_DRAW);

//Bind the EBO and set the indices
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indices.size(), &indices.at(0), GL_STATIC_DRAW);

这就是他们在您链接的网格页面上的方式。我在使用不同的装载机时遇到了类似的问题。您的索引未正确加载,因此某些顶点的位置正确,而另一些则不正确。

【讨论】:

  • 这与问题中已经存在的代码有什么不同吗?只改变顺序肯定不会有什么不同。
  • 我按照你说的移动了代码,但是对渲染的茶壶没有影响
【解决方案2】:

我对 OpenGL 的体验很糟糕,所以我可能会误会。我看到你的顶点是:x,y,z,nx,ny,nz 其中 xyz 是顶点坐标,nxnynz 是正常坐标。因此步幅为 6*sizeof(float)。

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), 0);

glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (void*) 3*sizeof(float);

只需在此处查看第二个答案:Opengl Vertex attribute stride 以了解有关步幅计算的更多信息

如果这不能帮助检查索引是否正确形成

还有一个小建议:使用立方体而不是茶壶(只需在搅拌机中制作立方体或自己在记事本中编写)

【讨论】:

    【解决方案3】:

    步幅应该是sizeof(Vertex)。如果它不能与那个步幅一起工作,那么还有其他问题!

    【讨论】:

      猜你喜欢
      • 2018-07-23
      • 1970-01-01
      • 1970-01-01
      • 2014-08-27
      • 2021-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-26
      相关资源
      最近更新 更多