std :: vector和VBO只渲染最后一个形状

时间:2016-11-29 03:39:27

标签: c++ opengl vector shader

我遇到了一个奇怪的问题。基本上我根据标志有网格类,我可以画一个点,一条线或一个三角形。例如,如果我想绘制两行,我可以执行以下操作

let ImageNames = ["esbn": "http://www.imgs.ie/uploads/banners/esb1.png"]

func getImageNamed(imageName: String?) -> NSURL? {
    if let urlstring = ImageNames[imageName ?? ""] {
        return NSURL(string: urlstring)
    } else {
        return nil
    }
}

imageURL = getImageNamed(imageName: imageName)

结果是

enter image description here

现在我想使用 Vertex vertices1[] = { Vertex(glm::vec3(-.5, -.5, 0)), Vertex(glm::vec3( 0, .5, 0)) }; Vertex vertices2[] = { Vertex(glm::vec3( .5, -.5, 0)), Vertex(glm::vec3( -.5, .5, 0)) }; Mesh mesh1(vertices1, sizeof(vertices1)/sizeof(vertices1[0]), 'L'); Mesh mesh2(vertices2, sizeof(vertices2)/sizeof(vertices2[0]), 'L'); // Rendering Loop: while( Window.isOpen() ){ ... //================( Rendering )========================= ourShader.Use(); mesh1.draw(); mesh2.draw(); //====================================================== ... } 并循环浏览网格。我的尝试如下

std::vector<Mesh>

使用上述方法,仅绘制最后一行,这是结果

enter image description here

此外,一旦我使用std::vector<Mesh> meshes; meshes.push_back(mesh1); meshes.push_back(mesh2); while( Window.isOpen() ){ ... //================( Rendering )========================= ourShader.Use(); for ( int i(0); i < meshes.size(); ++i ) meshes[i].draw(); //====================================================== ... } 即使我没有循环遍历向量,也会绘制最后一行。我不明白为什么使用.push_back()会使渲染恶化。我甚至试过std::vector但没有运气。有什么建议吗?

编辑: 这是Mesh类的构造函数

meshes[0].draw()

着色器

#include <iostream>
#include <vector>
#include <glm/glm.hpp>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "display.h"
#include "keyboard.h"
#include "shader.h"

class Vertex
{
public:
    Vertex(const glm::vec3& p) : m_position(p)
    {}

private:
    glm::vec3 m_position;

};

class Mesh
{
public:
    Mesh(Vertex* vertices, unsigned int numVertices, const char& flag);
    ~Mesh();
    void draw();
private:
    enum{
        POSITION_VB,
        NUM_BUFFERS
    };

    GLuint m_vertexArrayObject;
    GLuint m_vertexArrayBuffers[NUM_BUFFERS];
    unsigned int m_drawCount;

    char m_flag;
};


Mesh::Mesh(Vertex* vertices, unsigned int numVertices, const char& flag) : m_flag(flag), m_drawCount(numVertices)
{

    glGenVertexArrays(1, &m_vertexArrayObject);
    glBindVertexArray(m_vertexArrayObject);

    glGenBuffers(NUM_BUFFERS, m_vertexArrayBuffers);
    glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffers[POSITION_VB]);
    glBufferData(GL_ARRAY_BUFFER, numVertices*sizeof(vertices[0]), vertices, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glBindVertexArray(0);
}

Mesh::~Mesh()
{
    glDeleteVertexArrays(1, &m_vertexArrayObject);
    glDeleteBuffers(1, m_vertexArrayBuffers);
}

void Mesh::draw()
{
    switch(m_flag)
    {
        case 'P':
            glBindVertexArray(m_vertexArrayObject);
            glDrawArrays(GL_POINTS, 0, m_drawCount);
            glBindVertexArray(0);
        break;
        case 'L':
            glBindVertexArray(m_vertexArrayObject);
            glDrawArrays(GL_LINES, 0, m_drawCount);
            glBindVertexArray(0);
        break;
        case 'T':
            glBindVertexArray(m_vertexArrayObject);
            glDrawArrays(GL_TRIANGLES, 0, m_drawCount);
            glBindVertexArray(0);
        break;
    }

}



int main(void)
{
    Display Window(800, 600, "OpenGL Window");
    Keyboard myKeyboard( Window.getWindowPointer() );

    Vertex vertices1[] = {
        Vertex(glm::vec3(-.5, -.5, 0)),
        Vertex(glm::vec3(  0,  .5, 0))
    };

    Vertex vertices2[] = {
        Vertex(glm::vec3(  .5, -.5, 0)),
        Vertex(glm::vec3( -.5,  .5, 0))
    };

    Mesh mesh1(vertices1, sizeof(vertices1)/sizeof(vertices1[0]), 'L');
    Mesh mesh2(vertices2, sizeof(vertices2)/sizeof(vertices2[0]), 'L');

    std::vector<Mesh> meshes;
    meshes.emplace_back(mesh1);
    meshes.emplace_back(mesh2);
    std::cout << meshes.size() << std::endl;
    //*****************( SHADER )************************
    Shader ourShader("shader.vs", "shader.frag");

    glEnable(GL_PROGRAM_POINT_SIZE);


    while( Window.isOpen() ){
        Window.PollEvents();
        Window.clear();

        //================( Rendering )=========================
        ourShader.Use();
        //mesh1.draw();
        //mesh2.draw();
        for ( int i(0); i < meshes.size(); ++i )
            meshes[i].draw();
        //meshes[0].draw();
        //meshes[1].draw();
        //======================================================

        Window.SwapBuffers();
    }



    glfwTerminate();
    return 0;
}

2 个答案:

答案 0 :(得分:1)

修改

主要问题是,将Mesh对象添加到向量时会调用析构函数,因此底层数据会被清除。 进一步阅读:Why does my class's destructor get called when I add instances to a vector? | What is The Rule of Three?

我会亲自为init_buffers课程创建单独的free_buffersMesh方法,并正确使用它们。 (在获取OpenGL上下文后初始化缓冲区,在窗口关闭时释放缓冲区。) 这样,您可以在实际拥有OpenGL上下文之前开始构建网格(并将它们添加到场景中)。

我已经实现了缺少的代码部分,并使用CLF使用GLFW尝试了您的代码。

有效。请参阅此处的代码/ CLion项目:OpenGLSandbox/main.cpp

我添加的唯一代码基本上就是这些,所以轮到你找出差异/错误了。

// Constants
const size_t NUM_BUFFERS = 1;
const size_t POSITION_VB = 0;

// Vertex class
class Vertex {
private:
    glm::vec3 mCoords;
public:
    Vertex(glm::vec3 coords) : mCoords(coords) {};
};

// Mesh class
class Mesh {
private:
    GLuint m_vertexArrayObject;
    char m_flag;
    unsigned int m_drawCount;
    GLuint m_vertexArrayBuffers[NUM_BUFFERS];
public:
    /* your ctor and draw method */
}

Output sample

答案 1 :(得分:1)

我怀疑,问题在于(缺少)复制构造函数。默认值只是复制所有成员。因此,即使在您设法绘制任何内容之前,您的VAO和缓冲区也会被多次删除(向量在重新分配期间移动,如果他们无法移动它们进行复制)。根据经验:如果你有一个非默认的析构函数,你还必须实现一个复制构造函数和赋值运算符,或者如果你的类不是可复制的,那么显式删除它们。

对于您的具体案例,解决方案是:

  1. 快速解决方案:在向量中存储指向网格的指针:

    std::vector<Mesh*> meshes;
    meshes.emplace_back(&mesh1);
    meshes.emplace_back(&mesh2);
    
  2. 正确的解决方案:使用适当的RAII进行资源管理。使用here您的MCVE代码变为:

    class Mesh
    {
    public:
        Mesh(Vertex* vertices, unsigned int numVertices, const char& flag);
        void draw();
    private:
        //...
        GLvertexarray m_vertexArrayObject;
        GLbuffer m_vertexArrayBuffers[NUM_BUFFERS];
        unsigned int m_drawCount;
        char m_flag;
    };
    
    
    Mesh::Mesh(Vertex* vertices, unsigned int numVertices, const char& flag) : m_flag(flag), m_drawCount(numVertices)
    {
        GLuint id;
        glGenVertexArrays(1, &id);
        glBindVertexArray(id);
        m_vertexArrayObject.reset(id);
        for(int i = 0; i < NUM_BUFFERS; ++i)
        {
            glGenBuffers(1, &id);
            glBindBuffer(GL_ARRAY_BUFFER, id);
            m_vertexArrayBuffers[i].reset(id);
            glBufferData(GL_ARRAY_BUFFER, numVertices*sizeof(vertices[0]), vertices, GL_STATIC_DRAW);
        }
    
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
        glBindVertexArray(0);
    }
    
    void Mesh::draw()
    {
        switch(m_flag)
        {
            case 'P':
                glBindVertexArray(m_vertexArrayObject.get());
                glDrawArrays(GL_POINTS, 0, m_drawCount);
                glBindVertexArray(0);
            break;
            case 'L':
                glBindVertexArray(m_vertexArrayObject.get());
                glDrawArrays(GL_LINES, 0, m_drawCount);
                glBindVertexArray(0);
            break;
            case 'T':
                glBindVertexArray(m_vertexArrayObject.get());
                glDrawArrays(GL_TRIANGLES, 0, m_drawCount);
                glBindVertexArray(0);
            break;
        }
    
    }
    
    int main()
    {
        //...
    
        Mesh mesh1(vertices1, sizeof(vertices1)/sizeof(vertices1[0]), 'L');
        Mesh mesh2(vertices2, sizeof(vertices2)/sizeof(vertices2[0]), 'L');
    
        std::vector<Mesh> meshes;
        meshes.emplace_back(std::move(mesh1));
        meshes.emplace_back(std::move(mesh2));
    
        // ...
    
        return 0;
    }
    

    注意如何不再需要定义析构函数,并且您的类自动变为可移动但不可复制。此外,如果您有OpenGL 4.5或ARB_direct_state_access,那么事情会更简单。