問題描述
我為 OpenGL 3.3 創(chuàng)建了一個 Mesh 類,當我使用非默認構(gòu)造函數(shù)創(chuàng)建類時,當我創(chuàng)建對象時創(chuàng)建頂點時,它可以正常工作.
但是,我現(xiàn)在想要通過將它們放入向量中來動態(tài)創(chuàng)建多個對象,因此我必須添加一個默認構(gòu)造函數(shù),我使用與其他構(gòu)造函數(shù)相同的函數(shù)來設置緩沖區(qū)數(shù)據(jù)......但是它不起作用.據(jù)我所知,不是因為它在向量中,而是與構(gòu)造函數(shù)有關,或者與稍后創(chuàng)建緩沖區(qū)數(shù)據(jù)的事實有關.我真的不太確定.
I created a Mesh class for OpenGL 3.3, it works fine when I create the class with a non-default constructor, when I create the vertices when I create the object.
However, I now want to have multiple objects that I can create dynamically by putting them in a vector, so I had to add in a default constructor I use the same functions for setting up the buffer data as with the other constructor... but it doesn't work. It's as far as I can tell not because of the fact it's in the vector but it's something to do with the constructor or something with the fact the buffer data is created later. I'm really not quite sure.
這是我的課程.(當我創(chuàng)建一個有效的網(wǎng)格時,我調(diào)用帶參數(shù)的構(gòu)造函數(shù),當它不起作用時,我構(gòu)建一個沒有參數(shù)的網(wǎng)格并調(diào)用changeMes??h"函數(shù))
Here are my classes. ( When I create a mesh that works I call the constructor with parameters and when it doesn't work I construct a mesh with no parameters and call the "changeMesh" function)
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);
}
推薦答案
雖然您認為問題與將對象存儲在向量中無關,但我有一種強烈的感覺,它可能會發(fā)生.您在 C++ 包裝器中封裝 OpenGL 對象的方式是一種痛苦的方法,您可能會像之前的許多人一樣發(fā)現(xiàn).
While you believe that the problem has nothing to do with storing the objects in a vector, I have a strong feeling that it probably does. The way you are encapsulating OpenGL objects in C++ wrappers is a recipe for pain, and you're probably finding out like many did before you.
典型的問題是由復制和銷毀對象時發(fā)生的組合引起的.C++包裝器擁有的OpenGL對象在析構(gòu)函數(shù)中被刪除:
The typical problems are caused by the combination of what happens when objects are copied and destructed. The OpenGL objects owned by the C++ wrapper are deleted in the destructor:
mesh::~mesh()
{
glDeleteBuffers(1, &elementBuffer);
glDeleteBuffers(1, &vertexBuffer);
glDeleteVertexArrays(1, &vertexArrayObject);
}
為了說明這個問題,讓我們看一個典型的序列.假設您有一個網(wǎng)格對象向量,以及一種向該向量添加新網(wǎng)格的方法(注釋點以供以后參考):
To illustrate the problem with this, let's look at a typical sequence. Let's say you have a vector of mesh objects, and a method to add a new mesh to this vector (points annotated for later reference):
std::vector<mesh> m_meshes;
void createMesh(...) {
mesh newMesh; // point 1
newMesh.changeMesh(...);
m_meshes.push_back(newMesh); // point 2
} // point 3
看起來無害?根本不是.這里發(fā)生了不好的事情:
Looks harmless? It's not at all. Bad things happened here:
- 第 1 點:創(chuàng)建了新對象.構(gòu)造函數(shù)創(chuàng)建 OpenGL 對象,并將它們的名稱存儲在成員變量中.
- 第 2 點:將網(wǎng)格對象的副本添加到向量中,其中使用默認復制構(gòu)造函數(shù)創(chuàng)建副本.這意味著將復制包含 OpenGL 對象名稱的成員變量.
- 第 3 點:網(wǎng)格對象超出范圍.調(diào)用析構(gòu)函數(shù),刪除 OpenGL 對象.
- Point 1: New object is created. The constructor creates the OpenGL objects, and stores their names in member variables.
- Point 2: A copy of the mesh object is added to the vector, where the copy is created with the default copy constructor. This means that the member variables, which contain the OpenGL object names, are copied.
- Point 3: The mesh object goes out of scope. The destructor is invoked, which deletes the OpenGL objects.
畢竟你擁有的是一個存儲在向量中的網(wǎng)格對象,OpenGL 對象名稱存儲在其成員變量中,而實際的 OpenGL 對象已被刪除.這意味著存儲在此網(wǎng)格對象中的對象名稱現(xiàn)在無效.
What you have after all of this is a mesh object stored in the vector, with OpenGL object names stored in its member variables, while the actual OpenGL objects have been deleted. This means that the object names stored in this mesh object are now invalid.
根本問題是您的類沒有正確的復制構(gòu)造函數(shù)和賦值運算符.不幸的是,在成員變量中存儲 OpenGL 對象名稱,以及在構(gòu)造函數(shù)/析構(gòu)函數(shù)中生成/刪除對象名稱時,實現(xiàn)它們并不容易.
The root problem is that your class does not have proper copy constructors and assignment operators. And unfortunately, it is not easily possible to implement them when storing OpenGL object names in member variables, and generating/deleting the object names in constructor/destructor.
有很多方法可以解決這個問題.它們都不是完美的:
There are a number of ways to handle this. None of them are perfectly pretty:
不要在構(gòu)造函數(shù)/析構(gòu)函數(shù)中生成/刪除 OpenGL 對象.相反,使用您顯式調(diào)用的某種形式的
init()
/cleanup()
方法.缺點是您必須小心正確地調(diào)用這些方法.例如,如果您有一個對象向量,并且想要刪除該向量,則必須手動對向量的所有成員調(diào)用cleanup()
.
Do not generate/delete the OpenGL objects in constructor/destructor. Instead, use some form of
init()
/cleanup()
methods that you invoke explicitly. The downside is that you have to be careful to invoke these methods correctly. For example, if you have a vector of objects, and want to delete the vector, you have to invokecleanup()
on all members of the vector manually.
總是用指針來引用對象.使用網(wǎng)格對象指針向量代替網(wǎng)格對象向量.這樣,對象就不會被復制.您還必須小心正確地管理對象的生命周期,而不是泄漏它們.如果您使用某種形式的智能指針而不是裸指針,這是最簡單的.
Always reference the objects with pointers. Instead of having a vector of mesh objects, use a vector of mesh object pointers. This way, the objects are not copied. You also have to be careful to manage the lifetime of the objects correctly, and not leak them. This is easiest if you use some form of smart pointer instead of naked pointers.
使用某種形式的混合,您仍然使用實際的 C++ 對象,但它們將底層 OpenGL 對象的名稱存儲在引用計數(shù)的嵌套對象中.這樣,他們就可以實現(xiàn)正確的復制/分配語義.
Use some form of hybrid, where you still use actual C++ objects, but they store the names of the underlying OpenGL objects in a nested object that is reference counted. This way, they can implement proper copy/assign semantics.
我認為最簡單、最干凈的方法是使用智能指針的選項 2.較新版本的 C++ 在標準庫中具有智能指針,因此您無需實現(xiàn)任何內(nèi)容.例如,在 C++11 中,您可以使用類型 std::shared_ptr
來引用您的網(wǎng)格對象.上面的代碼片段將如下所示:
I think the easiest and cleanest approach is option 2 with using smart pointers. Newer versions of C++ have smart pointers in the standard library, so there isn't anything you need to implement. For example in C++11, you can use the type std::shared_ptr<mesh>
to reference your mesh objects. The code fragment above would then look like this:
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);
}
為了確保無論如何都不會意外復制對象,為類聲明未實現(xiàn)的(私有)復制構(gòu)造函數(shù)和賦值運算符也是一個好主意.本主題解釋了如何在 C++11 中做到最好:在 C++11 中使用顯式刪除的成員函數(shù),是否仍然值得從不可復制的基類繼承?.
To be sure that you don't accidentally copy the objects anyway, it's also a good idea to declare unimplemented (private) copy constructors and assignment operators for the class. This topic explains how to do that best in C++11: With explicitly deleted member functions in C++11, is it still worthwhile to inherit from a noncopyable base class?.
這篇關于使用默認構(gòu)造函數(shù)調(diào)用的網(wǎng)格類不起作用 OpenGL C++的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網(wǎng)!