使用默认构造函数调用的网格类不工作OpenGL C ++
我为OpenGL 3.3创build了一个Mesh类,当我使用非默认的构造函数创build类时,它可以很好地工作,当我创build对象时创build顶点。
然而,我现在想要有多个对象,我可以通过将它们放在一个向量中dynamic创build,所以我必须添加一个默认的构造函数,我使用相同的函数来设置缓冲区数据,与其他构造函数一样…但它不工作。 就我所知,不是因为它在向量中的事实,而是与构造函数有关,或者是稍后创build缓冲区数据的事情。 我真的不太确定。
这是我的课程。 (当我创build一个可以工作的网格时,我使用参数调用构造函数,当它不起作用时,我构造一个没有参数的网格并调用“changeMesh”函数)
mesh.h
#ifndef MESH_H #define MESH_H #include <iostream> #include <vector> #include <GL/glew.h> #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp> class mesh { public: mesh(); mesh(std::vector<GLfloat> vertices, std::vector<GLuint> triangles, GLuint shaderProgram); ~mesh(); void changeMesh(std::vector<GLfloat> vertices, std::vector<GLuint> triangles, GLuint shaderProgram); void render(); void Translate(glm::vec3 addVector); void Rotate(glm::vec3 rotVector, GLfloat angle); protected: private: GLuint vertexArrayObject, vertexBuffer, elementBuffer, shaderProgram; std::vector<GLfloat> vertices; std::vector<GLuint> indices; glm::mat4 transform; void setUpMesh(); void bindVertices(); }; #endif // MESH_H
mesh.cpp
#include "../include/mesh.h" mesh::mesh(std::vector<GLfloat> vertices, std::vector<GLuint> indices, GLuint shaderProgram) { this->shaderProgram = shaderProgram; this->vertices = vertices; this->indices = indices; setUpMesh(); } mesh::mesh(){ glGenVertexArrays(1, &vertexArrayObject); glBindVertexArray(vertexArrayObject); glGenBuffers(1, &vertexBuffer); glGenBuffers(1, &elementBuffer); } mesh::~mesh() { glDeleteBuffers(1, &elementBuffer); glDeleteBuffers(1, &vertexBuffer); glDeleteVertexArrays(1, &vertexArrayObject); } void mesh::changeMesh(std::vector<GLfloat> vertices, std::vector<GLuint> triangles, GLuint shaderProgram){ this->shaderProgram = shaderProgram; this->vertices = vertices; this->indices = indices; bindVertices(); } void mesh::setUpMesh(){ glGenVertexArrays(1, &vertexArrayObject); glBindVertexArray(vertexArrayObject); glGenBuffers(1, &vertexBuffer); glGenBuffers(1, &elementBuffer); bindVertices(); glBindVertexArray(0); } void mesh::bindVertices(){ glBindVertexArray(vertexArrayObject); glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(GLfloat), this->vertices.data(), GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->indices.size() * sizeof(GLuint), this->indices.data(), GL_STATIC_DRAW); GLint amountDataPerVert = 5; glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, amountDataPerVert*sizeof(GLfloat), 0); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, amountDataPerVert*sizeof(GLfloat), (void*)(3*sizeof(GLfloat))); glBindVertexArray(0); } void mesh::render(){ glBindVertexArray(vertexArrayObject); glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "transform"), 1, GL_FALSE, glm::value_ptr(transform)); glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); glBindVertexArray(0); } void mesh::Translate(glm::vec3 addVector){ transform = glm::translate(transform, addVector); } void mesh::Rotate(glm::vec3 rotVector, GLfloat angle){ transform = glm::rotate(transform, glm::radians(angle), rotVector); }
虽然你认为这个问题与存储vector无关,但我有一种强烈的感觉,可能是这样的。 在C ++包装器中封装OpenGL对象的方式是痛苦的秘诀,而且你可能会发现像在你之前所做的那样。
典型的问题是由于复制和销毁对象时发生的情况而导致的。 C ++包装器所拥有的OpenGL对象在析构函数中被删除:
mesh::~mesh() { glDeleteBuffers(1, &elementBuffer); glDeleteBuffers(1, &vertexBuffer); glDeleteVertexArrays(1, &vertexArrayObject); }
为了说明这个问题,我们来看一个典型的序列。 假设您有一个网格对象的vector,并且向这个vector添加一个新的网格的方法(为以后参考注释的点):
std::vector<mesh> m_meshes; void createMesh(...) { mesh newMesh; // point 1 newMesh.changeMesh(...); m_meshes.push_back(newMesh); // point 2 } // point 3
看起来无害? 这根本不是。 坏事发生在这里:
- 要点1:创build新的对象。 构造函数创buildOpenGL对象,并将其名称存储在成员variables中。
- 第2点: 将网格对象的副本添加到vector中,其中使用默认的复制构造函数创build副本。 这意味着将复制包含OpenGL对象名称的成员variables。
- 点3:网格对象超出范围。 调用析构函数,删除OpenGL对象。
所有这一切都是存储在向量中的网格对象,其OpenGL对象名称存储在其成员variables中,而实际的OpenGL对象已被删除。 这意味着存储在这个网格对象中的对象名称现在是无效的。
根本问题是你的类没有合适的拷贝构造函数和赋值操作符。 不幸的是,将OpenGL对象名称存储在成员variables中,并在构造函数/析构函数中生成/删除对象名称时,实现它们是不容易的。
有很多方法可以解决这个问题。 他们都不是很漂亮
-
不要在构造函数/析构函数中生成/删除OpenGL对象。 相反,请使用您明确调用的某种forms的
init()
/cleanup()
方法。 缺点是你必须小心正确地调用这些方法。 例如,如果您有一个对象向量,并且想要删除该向量,则必须手动调用向量的所有成员上的cleanup()
。 -
始终用指针引用对象。 使用网格对象指针的vector来代替网格对象的vector。 这样,对象不被复制。 您还必须小心地正确pipe理对象的生命周期,而不是泄漏它们。 这是最简单的,如果你使用某种forms的智能指针,而不是裸指针。
-
使用某种forms的混合,仍然使用实际的C ++对象,但它们将底层OpenGL对象的名称存储在引用计数的嵌套对象中。 这样,他们可以实现适当的复制/分配语义。
我认为最简单和最干净的方法是使用智能指针的选项2。 较新版本的C ++在标准库中有智能指针,所以不需要实现任何东西。 例如在C ++ 11中,可以使用std::shared_ptr<mesh>
来引用网格对象。 上面的代码片段将如下所示:
std::vector<std::shared_ptr<mesh> > m_meshes; void createMesh(...) { std::shared_ptr<mesh> newMesh = std::make_shared<mesh>(); newMesh->changeMesh(...); m_meshes.push_back(newMesh); }
为了确保您不会意外地复制对象,最好为该类声明未实现(私有)复制构造函数和赋值运算符。 本主题将解释如何在C ++ 11中做到最好:在C ++ 11中显式删除成员函数,是否仍然值得从不可复制的基类inheritance? 。