我有以下代码
class Mesh
{
public:
Mesh();
Mesh(std::vector<Vertex> vertices, std::vector<GLuint> indices);
~Mesh();
void draw(Shader& shader);
private:
std::vector<Vertex> mVertices;
std::vector<GLuint> mIndices;
GLuint mVBO;
GLuint mEBO;
};
Mesh::Mesh(std::vector<Vertex> vertices, std::vector<GLuint> indices)
{
mIndices = indices;
glGenBuffers(1, &mEBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mEBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mIndices.size() * sizeof(GLuint), &mIndices[0], GL_STATIC_DRAW);
mVertices = vertices;
glGenBuffers(1, &mVBO);
glBindBuffer(GL_ARRAY_BUFFER, mVBO);
glBufferData(GL_ARRAY_BUFFER, mVertices.size() * sizeof(Vertex), &mVertices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
computeBoundingBox();
}
Mesh::~Mesh()
{
glDeleteBuffers(1, &mVBO);
glDeleteBuffers(1, &mEBO);
}
void Mesh::draw(Shader& shader)
{
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mEBO);
glBindBuffer(GL_ARRAY_BUFFER, mVBO);
GLuint vpos = glGetAttribLocation(shader.program(), "vPosition");
GLuint vnor = glGetAttribLocation(shader.program(), "vNormal");
glVertexAttribPointer(vpos, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
glVertexAttribPointer(vnor, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)sizeof(Vector));
shader.bind();
glEnableVertexAttribArray(vpos);
glEnableVertexAttribArray(vnor);
glDrawElements(GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, 0);
shader.unbind();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void loadSquare(Mesh& mesh)
{
std::vector<Vertex> vertices;
vertices.push_back(Vertex(Vector(0.5f, 0.5f, 0.f), Vector(1.f, 0.f, 0.f)));
vertices.push_back(Vertex(Vector(-0.5f, 0.5f, 0.f), Vector(0.f, 1.f, 0.f)));
vertices.push_back(Vertex(Vector(-0.5f, -0.5f, 0.f), Vector(0.f, 0.f, 1.f)));
vertices.push_back(Vertex(Vector(0.5f, -0.5f, 0.f), Vector(1.f, 0.f, 1.f)));
std::vector<GLuint> indices;
indices.push_back(0);
indices.push_back(1);
indices.push_back(2);
indices.push_back(0);
indices.push_back(2);
indices.push_back(3);
mesh = Mesh(vertices, indices);
}
int main(int argc, char** argv)
{
// Create opengl context and window
initOGL();
// Create shaders
Shader shader("render.vglsl", "render.fglsl");
Mesh mesh;
loadSquare(mesh);
while (!glfwWindowShouldClose(window))
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
mesh.draw(shader);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
如果我尝试运行它,它只会在它创建的窗口上显示灰色图像。
使用调试器跟踪应用程序后,当它到达行mesh = Mesh(vertices, indices)
时,它会为OpenGL创建缓冲区并将顶点和索引std :: vectors复制到作为参数传递的变量mesh
。
但是,它还调用由Mesh(vertices,indices)
创建的对象的析构函数,这反过来使OpenGL上下文中的缓冲区无效,因此当应用程序到达mesh.draw(shader)
时,缓冲区网格点将不再有效。
move-constructor是否可以帮助我解决我的问题,即避免调用Mesh(vertices,indices)
的析构函数?还有其他解决方案吗?
答案 0 :(得分:4)
从您的来源,您正在绑定到立即销毁的矢量。
基本上,在loadSquare函数中,当你在loadSquare的最后一行中的赋值右侧写 Mesh(vertices,indices); 时,你正在创建一个网格并绑定它。
mesh = Mesh(vertices, indices);
认为这样的一行:
...
Mesh m1(vertices, indices); // a
mesh= m1; // b
// m1 gets destroyed here.
}
线(a)创建并绑定网格。
当你将它分配给第b行的网格时,mesh.mVertices和mesh.mIndices将获得向量的副本,mVBO和mEBO将获得绑定值的副本。
将该行(b)视为写作
mesh.mVertices= m1.mVertices; // mesh gets a new vector with the same values
mesh.mIndices= m1.mIndices; // mesh gets a new vector with the same values
mesh.mVBO= m1.mVBO;
mesh.mEBO= m1.mEBO;
在loadSquare()结束时,m1将被销毁(析构函数被调用)。
在你的调用函数中,你将得到包含mVBO和mEBO成员的网格,这些成员绑定到被销毁的向量。它包含自己的向量,它们具有相同的值,但它们是从不绑定的不同内存位置的副本。
有多种方法可以解决这个问题,例如通过指针返回方形网格。或编写一个赋值运算符(google for shallow copy)。
但我的建议是创建一个空构造函数和一个额外的fillMesh函数,就像你当前的cotstructor一样。
Mesh::Mesh(void); // set mVBO and mEBO to zero.
void Mesh::fillMesh(std::vector<Vertex> vertices, std::vector<GLuint> indices); // same code as your current constructor.
然后重写你的loadSquare函数:
void loadSquare(Mesh& mesh)
{
std::vector<Vertex> vertices;
vertices.push_back(Vertex(Vector(0.5f, 0.5f, 0.f), Vector(1.f, 0.f, 0.f)));
vertices.push_back(Vertex(Vector(-0.5f, 0.5f, 0.f), Vector(0.f, 1.f, 0.f)));
vertices.push_back(Vertex(Vector(-0.5f, -0.5f, 0.f), Vector(0.f, 0.f, 1.f)));
vertices.push_back(Vertex(Vector(0.5f, -0.5f, 0.f), Vector(1.f, 0.f, 1.f)));
std::vector<GLuint> indices;
indices.push_back(0);
indices.push_back(1);
indices.push_back(2);
indices.push_back(0);
indices.push_back(2);
indices.push_back(3);
mesh.fillMesh(vertices, indices);
}
因此,loadSquare将创建顶点和索引,并将它们从调用函数设置到网格中并绑定它们。
附加说明(清洁解决方案):
答案 1 :(得分:1)
当您实例化Mesh(vestices,indices)时,将调用Mesh构造函数。这只是一个临时对象实例。然后代码调用赋值运算符将临时对象实例复制到网格变量中。由于您还没有定义operator = [给定提供的代码],它会执行默认的赋值行为。完成该赋值后,将调用~Mesh析构函数。
答案 2 :(得分:1)
你违反了rule of three (rule of five),当然你在完成任务后会遇到问题。在这一行:
mesh = Mesh(vertices, indices);
您创建一个在语句后立即销毁的临时对象。因此,正确实施或禁止复制ctor和复制赋值运算符来解决问题。您可能还希望实现move ctor和移动赋值运算符,尤其是在禁止复制时。