在OpenGL中绘制VBO不起作用

Drawing VBOs in OpenGL not working

本文关键字:VBO 不起作用 绘制 OpenGL      更新时间:2023-10-16

我正在编写一个简单的类,它解析OBJ并在此之后呈现OBJ。我已经写了一个从RAM绘制顶点数组的方法,我想对它进行一些优化(我读到使用VBO可以使帧速率提高x4倍!)。

遗憾的是,我失败了,我遵循了一堆教程,但都无济于事。这是语法分析器和在GPU上存储顶点和索引缓冲区的调用,我已经调试了语法分析器本身,这很好。vertexBuffer和vertexIndices都加载得很好(我已经打印了它们,并根据obj文件手动检查了它们)。

Mesh::Mesh(string path) {
float* normals;
float* faces;
float* textures;
string fullpath = path + ".obj";
this->mats = loadMtl(path); //Loads a Material list, not used now drawing white
string line;
ifstream objFile(fullpath.c_str());
if (objFile.is_open()) {
objFile.seekg(0, ios::end);
long fileSize = long(objFile.tellg());
objFile.seekg(0, ios::beg);
// Init buffers and indices
unsigned int inserted = 0;
string currentMtl = "";
float* vertexBuffer = new float[fileSize];
float* textureBuffer = new float[fileSize];
float* normalBuffer = new float[fileSize];
unsigned int* vertexIndices = new unsigned int[fileSize];
unsigned int* normalIndices = new unsigned int[fileSize];
unsigned int* textureIndices = new unsigned int[fileSize];
int connectedVert = 0;
int textureCoords = 0;
int normalsCount = 0;
int vertexIndex = 0;
string prefix = "usemtl";
while (!objFile.eof()) {
getline(objFile, line);
if (line.c_str()[0] == 'v' && line.c_str()[1] == ' ') {
line[0] = ' ';
sscanf_s(line.c_str(), "%f %f %f ",
&vertexBuffer[connectedVert],
&vertexBuffer[connectedVert + 1],
&vertexBuffer[connectedVert + 2]);
connectedVert += 3;
}
else if (line.c_str()[0] == 'v' && line.c_str()[1] == 't') {
line[0] = ' ';
line[1] = ' ';
sscanf_s(line.c_str(), "%f %f ",
&textureBuffer[textureCoords],
&textureBuffer[textureCoords + 1]);
textureCoords += 2;
}
else if (line.c_str()[0] == 'v' && line.c_str()[1] == 'n') {
line[0] = ' ';
line[1] = ' ';
sscanf_s(line.c_str(), "%f %f %f ",
&normalBuffer[normalsCount],
&normalBuffer[normalsCount + 1],
&normalBuffer[normalsCount + 2]);
normalsCount += 3;
}
else if (line.c_str()[0] == 'f') {
line[0] = ' ';
if (textureCoords > 0) {
sscanf_s(line.c_str(), "%i/%i/%i %i/%i/%i %i/%i/%i",
&vertexIndices[vertexIndex], &textureIndices[vertexIndex], &normalIndices[vertexIndex],
&vertexIndices[vertexIndex + 1], &textureIndices[vertexIndex +1], &normalIndices[vertexIndex +1],
&vertexIndices[vertexIndex + 2], &textureIndices[vertexIndex +2], &normalIndices[vertexIndex +2]);
}
else {
sscanf_s(line.c_str(), "%i//%i %i//%i %i//%i",
&vertexIndices[vertexIndex], &normalIndices[vertexIndex],
&vertexIndices[vertexIndex+1], &normalIndices[vertexIndex +1],
&vertexIndices[vertexIndex+2], &normalIndices[vertexIndex +2]);
}
for (int j = 0; j < 3; j++) {
vertexIndices[vertexIndex+j] -= 1;
normalIndices[vertexIndex +j] -= 1;
textureIndices[vertexIndex +j] -= 1;
}
vertexIndex += 3;
}
else if (!strncmp(line.c_str(), prefix.c_str(), strlen(prefix.c_str()))) {
inserted++;
// No more vertices, normals or texture coords to load, gen arrays on GPU
if (currentMtl == "") {
//Copy vertices to GPU
glGenBuffers(1, &this->verticesID);
glBindBuffer(GL_ARRAY_BUFFER, this->verticesID);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * connectedVert, vertexBuffer, GL_STATIC_DRAW);
/*
//Won't be bothered with normals or textures right now...
//Copy normals to GPU
glGenBuffers(1, &this->normalsID);
glBindBuffer(GL_ARRAY_BUFFER, this->normalsID);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * normalsCount, &normalBuffer[0], GL_STATIC_DRAW);
//Copy UV to GPU
if (textureCoords > 0) {
glGenBuffers(1, &this->textID);
glBindBuffer(GL_ARRAY_BUFFER, this->textID);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * textureCoords, &textureBuffer[0], GL_STATIC_DRAW);
}*/
}
else {
// Filled up an index array, copy it.
this->indices.push_back(0);
this->faces.push_back(vertexIndex);
glGenBuffers(1, &this->indices[this->indices.size() - 1]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->indices[this->indices.size() - 1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * vertexIndex, vertexIndices, GL_STATIC_DRAW);
}
currentMtl = line.substr(7, line.length());
vertexIndex = 0;
}
}
// Copy the last indices buffer.
if (currentMtl != "" && indices.size() < inserted) {
this->indices.push_back(0);
this->faces.push_back(vertexIndex);
glGenBuffers(1, &this->indices[this->indices.size() - 1]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->indices[this->indices.size() - 1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * vertexIndex, vertexIndices, GL_STATIC_DRAW);
}
objFile.close();
delete[] vertexBuffer;
delete[] textureBuffer;
delete[] normalBuffer;
delete[] vertexIndices;
delete[] normalIndices;
delete[] textureIndices;
}
else {
throw runtime_error("Unable to load obj file: " + path + ".obj");
}
}

最后,这是抽签通知。我猜问题就在这里,但我不能完全指出这个问题。我用glIntercept查看是否有什么问题,但我没有错误。

void Mesh::draw() {
//Binding Vertex array
glBindBuffer(GL_ARRAY_BUFFER, this->verticesID);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, (void*)0);
for (unsigned int i = 0; i < this->indices.size(); i++) {
glColor3f(1.0f, 1.0f, 1.0f); // Draw it in plain ol' white
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->indices[i]);
glDrawElements(GL_TRIANGLES,this->faces[i], GL_UNSIGNED_INT,(void*)0);
}
glDisableClientState(GL_VERTEX_ARRAY);
}

如果需要,下面是Mesh类声明:

class Mesh {
public:
GLuint verticesID, textID, normalsID;
vector<GLuint> indices;
vector<GLuint> faces;
vector<Material> mats;
public:
Mesh(string path);
void draw();
~Mesh();
};

将OpenGL主版本设置为2,将次版本设置为1。正如我之前提到的,我认为OpenGL设置正确,因为我能够在直接模式和使用RAM中的顶点指针绘制三角形。欢迎任何帮助:)

它是calssMesh的构造函数,读取文件并生成对象缓冲区(glGenBuffers)。在析构函数Mesh::~Mesh中,对象缓冲区被销毁(glDeleteBuffers)

但你把push_backMesh变成了std::vector。这意味着生成了一个临时Mesh对象,它读取文件并在其构造函数中生成对象缓冲区。此时,所有数据都是有效的,并且生成对象缓冲区(GPU)。当调用std::vector::push_back时,则在std::vector中生成新的Mesh对象。该对象由默认的复制构造函数构造,并获取第一个Mesh对象的所有成员的副本。在这之后,立即通过临时对象的析构函数Mesh::~Mesh破坏临时Mesh对象并且删除对象缓冲区(glDeleteBuffers)。此时,所有数据都不见了。

请参见std::vector::push_back。在destrutorMesh::~Mesh中放置一个断点,然后就可以简单地跟踪过期时间。

您不应该在类的构造函数和析构函数中生成和删除GPU对象,除非您使类不可复制且不能可复制:

class Mesh
{
Mesh(const Mesh &) = delete;
Mesh & operator = (const Mesh &) = delete;
....
};

由于调试原因,您可以通过替换来快速修复此行为

test.meshes.push_back(Mesh("tri"));

通过

test.meshes.emplace_back("tri");

(见std::vector::emplace_back)


但最后你应该做一些类似的事情:

vector<std::unique_ptr<Mesh>> meshes;
meshes.emplace_back(new Mesh("tri"));