我正在尝试更新我曾经使用OpenGL 2.x样式顶点数组的引擎来使用OpenGL 3.x,这意味着更新到VAO / VBO。我想我并没有正确地约束VBO。请阅读下面的更多信息或跳到代码并找到我做错了。
我的网格类的快速概述如下所示:
网
MeshNode
MeshObject
如果我绘制一个只有一个MeshObject的MeshNode,它似乎画得很好。当我绘制一个具有多个MeshObjects的MeshNode时,我得到的东西就是我想要绘制的模型的一般形状,但有点乱码。
我已经通过gDEbugger检查了Visual Studio调试器中的顶点数据和VBO数据,看起来一切都很好,所以我非常确定从文件加载到加载到VBO中是有效的。
我使用gDEbugger强制它为所有顶点而不是三角形绘制点,并且它具有单个MeshObject的形状,这使我相信我只是没有正确地绑定到不同的VBO。好像它试图用不同的索引绘制,但每次都是相同的顶点。
VertexData如下所示:
struct VertexData
{
enum
{
NUM_TEXCOORDS = 1,
};
vector3 vertex;
vector3 normal;
vector2 texCoord[NUM_TEXCOORDS];
};
相关的MeshNode代码:
void MeshNode::initVAO(void)
{
closeVAO();
unsigned int scan;
//init index data
if (m_meshObjects.size() > 0)
{
glGenVertexArrays(1, &m_meshVAO);
glBindVertexArray(m_meshVAO);
{
//add up the total index count for all the mesh objects in this node
unsigned int indexCount = 0;
for (scan = 0; scan < m_meshObjects.size(); ++scan)
{
indexCount = indexCount + m_meshObjects[scan].getIndices()->size();
}
//make the actual index buffer
glGenBuffers(1, &m_indexVBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexVBO);
{
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * sizeof(unsigned short), NULL, GL_STATIC_DRAW);
//set up VBOs and fill the index buffer with the index data from each mesh object
unsigned int offset = 0;
for (scan = 0; scan < m_meshObjects.size(); ++scan)
{
m_meshObjects[scan].initVBOs(offset);
}
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
glBindVertexArray(0);
}
for (scan = 0; scan < m_childMeshNodes.size(); ++scan)
{
m_childMeshNodes[scan]->initVAO();
}
}
void MeshNode::closeVAO(void)
{
if (m_meshVAO != 0)
{
glBindVertexArray(m_meshVAO);
{
glDeleteBuffers(1, &m_indexVBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
glBindVertexArray(0);
glDeleteVertexArrays(1, &m_meshVAO);
m_meshVAO = 0;
m_indexVBO = 0;
}
}
void MeshNode::render(const matrix4 &_parentTransform)
{
matrix4 transform = _parentTransform * m_transform;
if (m_meshObjects.size() > 0)
{
glBindVertexArray(m_meshVAO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexVBO);
{
for (unsigned int objectScan = 0; objectScan < m_meshObjects.size(); ++objectScan)
{
m_meshObjects[objectScan].render(transform);
}
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
for (unsigned int childScan = 0; childScan < m_childMeshNodes.size(); ++childScan)
{
m_childMeshNodes[childScan]->render(transform);
}
}
相关的MeshObject代码:
void MeshObject::initVBOs(unsigned int& _indexOffset)
{
//sub in this section of the index data
m_indexOffset = _indexOffset;
_indexOffset = _indexOffset + m_indices.size();
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, m_indexOffset * sizeof(unsigned short), m_indices.size() * sizeof(unsigned short), &(m_indices[0]));
//init vertex data
glGenBuffers(1, &m_vertexVBO);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexVBO);
{
glBufferData(GL_ARRAY_BUFFER, m_data.size() * sizeof(VertexData), &(m_data[0]), GL_STATIC_DRAW);
glVertexAttribPointer(Shader::POSITION, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), (char*)0);
glEnableVertexAttribArray(Shader::POSITION);
glVertexAttribPointer(Shader::NORMAL, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), (char*)12);
glEnableVertexAttribArray(Shader::NORMAL);
glVertexAttribPointer(Shader::TEXCOORD0, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData), (char*)24);
glEnableVertexAttribArray(Shader::TEXCOORD0);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void MeshObject::closeVBOs(void)
{
glDeleteBuffers(1, &m_vertexVBO);
m_vertexVBO = 0;
}
void MeshObject::render(const matrix4& _transform)
{
m_material->bind(_transform);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexVBO);
{
glEnableVertexAttribArray(Shader::POSITION);
glEnableVertexAttribArray(Shader::NORMAL);
glEnableVertexAttribArray(Shader::TEXCOORD0);
glDrawRangeElements(GL_TRIANGLES, m_indexOffset, m_indexOffset + m_indices.size(), m_indices.size(), GL_UNSIGNED_SHORT, (char*)0);
glDisableVertexAttribArray(Shader::POSITION);
glDisableVertexAttribArray(Shader::NORMAL);
glDisableVertexAttribArray(Shader::TEXCOORD0);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
答案 0 :(得分:5)
我的网格类的快速概述如下所示:
我认为你的场景图层次结构有点混乱。场景图中需要的所有Node
都是变换,Node
子项列表以及要在Node
中绘制的网格列表。它不应该有元素缓冲区或VAO;这些在概念上是网格数据的一部分。
事实上,你的问题源于最后一点。 Vertex Array Objects包含glVertexAttrib(I)Pointer
次调用设置的状态。这意味着,每次在同一个MeshNode中调用MeshObject::initVBOs
时,您都会覆盖上一次调用所设置的数据。
每个网格需要自己的VAO,而不是节点。网格可以共享索引数据,这就是你似乎正在做的事情(尽管如果你担心有太多的缓冲区,它们也应该为它们的顶点数据共享相同的缓冲区对象)。但VAO必须与众不同。多个VAO可以引用相同的元素缓冲区。
答案 1 :(得分:1)
我认为你错误地使用了glDrawRangeElements
。 start
和end
参数是索引数组中可能出现的最小和最大顶点索引,但是您提供了m_indexOffest
和m_indexOffset+m_indices.size()
,这是索引数据你渲染的范围,不一定是那些索引数组中顶点索引的范围。
作为旁注,VAO封装了每个顶点数组/缓冲区状态,所有缓冲区绑定,指针和启用的标志,因此您正在进行许多不必要的调用。只需在initVAO
方法中绑定索引缓冲区,然后在MeshNode::render
方法中绑定VAO时它总是被绑定(当然在这种情况下省略所有glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
调用)。同样,您只需要在initVAO
方法中启用attribtue数组(假设所有MeshObject
都启用了相同的属性),并且在绑定VAO时它们会自动启用。在您当前的配置中,VAO是完全不必要的,您不会以任何方式从中获取。