OBJ Loader 不会“创建”或“渲染”对象

Posted

技术标签:

【中文标题】OBJ Loader 不会“创建”或“渲染”对象【英文标题】:OBJ Loader does not 'create' or 'render' object 【发布时间】:2020-01-24 23:29:33 【问题描述】:

我想实现一个 obj 加载器,它可以从 blender 导出的 obj 文件中获取“v”(顶点)、“vt”(纹理)、“vn”(法线)和“f”(面)坐标在我的程序上渲染它。但是,在实施之后,我发现它存在问题。当我创建它时没有任何显示。现在,自从我开始,网格代码对我来说一直是个问题,所以我希望有人能找到我无法找到的东西。不仅如此,纹理代码状态(在终端中)Trying to access the pixels of an empty image。但是,我给它的图像有像素,我完全不知道为什么会这样。

// Chest.cpp 
objLoader.loadObjModel("res/models/test.obj");
mesh.loadToVAO(objLoader.getVertices(), objLoader.getTextures(), objLoader.getNormals(), objLoader.getIndices());
mesh.loadTexture(Texture::get("res/images/wallpaper.png"));
program = Program::get(BASIC_VERTEX_SHADER, BASIC_FRAGMENT_SHADER);

这会从test.obj 加载一个obj 模型,然后在mesh.loadToVAO 中获取顶点、纹理、法线和索引。然后网格也得到纹理位置。

// Mesh.cpp // Loading 
void Mesh::loadToVAO(std::vector<glm::vec3> vertices, std::vector<glm::vec2> textures, std::vector<glm::vec3> normals, std::vector<int> indices) 
    // create a VAO
    GLuint vaoID = createVAO();
    indicesSize = indices.size();
    bindIndicesBuffer(indices.data(), indicesSize);

    // Store the data in attribute lists
    storeDataInAttrubeList(0, 3, &vertices[0], vertices.size() * sizeof(glm::vec3));
    storeDataInAttrubeList(1, 2, &textures[0], textures.size() * sizeof(glm::vec2));
    storeDataInAttrubeList(2, 3, &normals[0], normals.size() * sizeof(glm::vec3));
    unbindVAO();


// Rendering
void Mesh::renderVAO() 
    // Texture
    texture->useTexture();

    // Binding
    glBindVertexArray(VAO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VBO);

    // Rendering
    glDrawElements(GL_TRIANGLES, indicesSize, GL_UNSIGNED_INT, 0);

    // Unbinding
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindVertexArray(0);


// createVAO/storeDataInAttrubeList/bindIndicesBuffer
GLuint Mesh::createVAO() 
    GLuint vaoID;

    glGenVertexArrays(1, &vaoID);
    VAO = vaoID;
    glBindVertexArray(vaoID);

    return vaoID;


void Mesh::storeDataInAttrubeList(GLuint attribNumber, int attribSize, void* data, int dataSize) 
    GLuint vboID;
    // Create a new buffer
    glGenBuffers(1, &vboID);
    // Store the buffer in the list
    VBO = vboID;
    // Bind the buffer to use it
    glBindBuffer(GL_ARRAY_BUFFER, vboID);
    // Store the data in the buffer
    glBufferData(GL_ARRAY_BUFFER, dataSize, data, GL_STATIC_DRAW);
    // Tell OpenGL how and where to store this VBO in the VAO
    glVertexAttribPointer(attribNumber, attribSize, GL_FLOAT, GL_FALSE, 0, nullptr);


void Mesh::bindIndicesBuffer(int* indices, int& count) 
    GLuint vboID;
    // Generate a buffer and bind it for use
    glGenBuffers(1, &vboID);
    // Store the buffer in the list
    VBO = vboID;
    // Bind the buffer to use it
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboID);
    // Store the indices in the buffer
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * count, indices, GL_STATIC_DRAW);
 

这会遍历每一行并找到合适的坐标。

while (fgets(line, 256, file) != NULL) 
        token = NULL;
        type = strtok_s(line, " ", &token);

        // Vertex
        if (type[0] == 'v' && type[1] == NULL) 
            x = strtod(token, &stop);
            token = stop + 1; 
            y = strtod(token, &stop);
            token = stop + 1; 
            z = strtod(token, &stop);

            vertices.push_back(glm::vec3(x, y, z));
        

        // Textures
        else if (type[0] == 'v' && type[1] == 't') 
            x = strtod(token, &stop);
            token = stop + 1; 
            y = 1 - strtod(token, &stop);

            tempTextures.push_back(glm::vec2(x, y));
        

        // Normals
        else if (type[0] == 'v' && type[1] == 'n') 
            x = strtod(token, &stop);
            token = stop + 1; 
            y = strtod(token, &stop);
            token = stop + 1; 
            z = strtod(token, &stop);

            tempNormals.push_back(glm::vec3(x, y, z));
        

        // Faces
        else if (type[0] == 'f') 
            if (indices.size() == 0) 
                // Set size of the array
                textures.resize(vertices.size());
                normals.resize(vertices.size());
            

            // Process vertices data
            processVertices(token, indices, tempTextures, textures, tempNormals, normals);
        
    
// This method process the vertice and indice information 
void OBJLoader::processVertices(char* vertexData, std::vector<int>& indices, std::vector<glm::vec2>& tempTextures, 
    std::vector<glm::vec2>& textures, std::vector<glm::vec3>& tempNormals, std::vector<glm::vec3>& normals) 

    char* stop;
    int vertexPointer;

    for (unsigned int i = 0; i < 3; i++) 
        // Get and store index
        vertexPointer = strtol(vertexData, &stop, 10) - 1;
        indices.push_back(vertexPointer);
        vertexData = stop + 1; 

        textures[vertexPointer] = tempTextures[strtol(vertexData, &stop, 10) - 1];
        vertexData = stop + 1; 

        normals[vertexPointer] = tempNormals[strtol(vertexData, &stop, 10) - 1];
        vertexData = stop + 1; 
    

Chest.cpp你可以看到我正在调用一个叫get(something)的方法,就是这个样子

std::vector<glm::vec3> getVertices()  return vertices;  // Note that vertices and etc. are vectors

最后,这不是渲染还是只是没有创建? 我知道代码相当长,但是我不完全知道哪些部分对我的问题最重要或最负责。

【问题讨论】:

【参考方案1】:

我不知道这是否会解决您代码中的所有问题,但我注意到了一些事情:

    Mesh::storeDataInAttrubeList() 确实为每个属性数组创建了一个新的 VBO(这在 GL 中是可能的,但您不需要这样做。将整个对象数据打包到一个 VBO 中)。但更糟糕的是它覆盖了(Mesh 类属性?)变量VBO。因此,您基本上会丢失之前创建的 VBO 的 ID。更糟糕的是,你后期渲染时使用VBO作为元素数组缓冲区,这会彻底搞砸你的渲染。 Mesh::renderVAO() 每次都会更改元素缓冲区绑定并将其重置为 0。这不是必需的。 GL_ELEMENT_ARRAY_BUFFER 绑定存储在 VAO 中,无需为每个绘制调用设置和重置它。 您的 OBJ 解析器未正确复制顶点。您需要为每个唯一的vertexID/texcoordID/normalID 三元组创建一个顶点。但是您所做的只是用新的 texcoords 和法线覆盖 vertexID 处的顶点数据,而不是创建一个全新的顶点。

【讨论】:

如果 vao's 和 vbo's 是一个向量,那会修复覆盖吗?如果是这样,对于像这样的代码 glBindVertexArray(VAO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VBO);我是否必须制作一个 for 循环,并且每个循环都是一个新的 vao/vbo? "如果 vao's 和 vbo's 是一个向量,那会修复覆盖吗?不,为什么会这样。你的算法是错误的。 “如果是这样,对于像这样的代码 glBindVertexArray(VAO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VBO); 我是否必须创建一个 for 循环并且每个循环都是一个新的 vao/vbo?”我不知道你在这里说什么。如果你有一个 OBJ 文件,你很可能会创建一个元素索引缓冲区,所以不需要循环。

以上是关于OBJ Loader 不会“创建”或“渲染”对象的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Android 中渲染 OBJ 或 FBX?

js对象创建

关于javascript中defineProperty的学习

js创建对象的几种方式

爱创课堂每日一题第五十一天- new操作符具体干了什么呢?

在对象属性上创建或添加数字的最佳方法