std::vector 和 VBO 仅渲染最后一个形状 [重复]
Posted
技术标签:
【中文标题】std::vector 和 VBO 仅渲染最后一个形状 [重复]【英文标题】:std::vector and VBOs render only the last shape [duplicate] 【发布时间】:2016-11-29 03:39:26 【问题描述】:我遇到了一个奇怪的问题。基本上我有 Mesh 类取决于一个标志,我可以画一个点、一条线或一个三角形。例如,如果我想画两条线,我可以这样做
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>
并遍历网格。我的尝试如下
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
会降低渲染效果。我什至尝试过meshes[0].draw()
,但没有运气。有什么建议吗?
编辑: 这是Mesh类的构造函数
#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;
着色器
#version 330 core
out vec4 color;
void main()
color = vec4(1.0f,0.5f,0.2f,1.0f);
#version 330 core
layout (location = 0) in vec3 position;
void main()
gl_PointSize = 10.0;
gl_Position = vec4(position, 1.0);
【问题讨论】:
您的代码看起来不错。如果没有Mesh
代码,很难说出问题所在。它是否包含 VAO 描述符?也许问题出在它的赋值运算符或复制构造函数上?如果您不创建 mesh1
和 mesh2
对象,而是使用带有适当参数的 std::vector::emplace
会发生什么?
@Sergey,是的,它确实包含 VAO。请查看更新。我试过std::vector::emplace
有同样的问题。
如果你以相反的顺序遍历你的向量......是否只绘制了第一个? 'draw'是否有可能擦除场景?
这可能是复制构造函数的问题。没有看到MCVE,很难远程解决您的问题。
@ybungalobill,我会尽快准备一份。
【参考方案1】:
正如我所怀疑的,问题出在(缺少)复制构造函数上。默认的只是复制所有成员。结果,您的 VAO 和缓冲区被多次删除,甚至在您设法绘制任何东西之前(向量在重新分配期间移动,如果它们不能移动,它们就会复制)。根据经验:如果你有一个非默认析构函数,你还必须实现一个复制构造函数和一个赋值运算符,或者如果你的类不是可复制的,则显式删除它们。
对于您的具体情况,解决方案是:
快速解决方案:将指向网格的指针存储在向量中:
std::vector<Mesh*> meshes;
meshes.emplace_back(&mesh1);
meshes.emplace_back(&mesh2);
正确的解决方案:使用适当的 RAII 进行资源管理。使用unique_ptr technique from 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
,那么事情会变得更加简单。
【讨论】:
【参考方案2】:编辑
主要问题是,当您将 Mesh
对象添加到向量时会调用析构函数,因此会清理底层数据。
进一步阅读:Why does my class's destructor get called when I add instances to a vector? | What is The Rule of Three?
我会亲自为我的Mesh
类创建单独的init_buffers
和free_buffers
方法并适当地使用它们。 (获取 OpenGL 上下文后初始化缓冲区,关闭窗口时释放缓冲区。)
这样您就可以在实际拥有 OpenGL 上下文之前开始构建网格(并将它们添加到场景中)。
我已经实现了缺少的代码部分,并使用 CLion 使用 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 */
【讨论】:
感谢您的回答。我可能会在我的 Mac 上编译相同的代码,看看这是否有什么不同。以上是关于std::vector 和 VBO 仅渲染最后一个形状 [重复]的主要内容,如果未能解决你的问题,请参考以下文章
OpenGL:将 VBO 与 std::vector 一起使用