OpenGL - 绘制存储在 VBO 中的大量信息
Posted
技术标签:
【中文标题】OpenGL - 绘制存储在 VBO 中的大量信息【英文标题】:OpenGL - Drawing Large Amounts of Information stored in VBO's 【发布时间】:2014-05-04 06:27:34 【问题描述】:我有相当大的 C++ 对象,它们将网格数据加载到内存中,然后根据 OnDisplay 回调进行绘制。
问题是刷新率真的很慢,我怀疑是因为我的代码写得不好。
无论如何;这是我的类的样子(显示的函数原型让您了解我的类是如何设置的)。
我想知道的是,如果我的大多数 VBO 没有改变并跳过我的开始和结束绘制函数,是否可以在内存中以某种方式调用“glDrawElements”函数,如下所示.
或者,甚至更好,
如果有一个神奇的 OpenGL 函数,我可以调用它,通过一次,OpenGL 可以渲染我所有未更改的缓冲区 ID,我可以专注于绘制已更改的缓冲区 ID 和相机?
大多数情况下,我只会让相机在场景中移动。
我根据教程和文档设置了这些功能,因此我知道它们可以工作;我只是想加快绘图速度,尤其是当我加载的网格大小为 100MB 以上时。
首先,这是我的类原型:
class MyMeshData
public:
MyMeshData();
~MyMeshData();
// Save up data into GPU buffers.
bool Initialize(const MeshDataFromFileClass * StaticMeshData);
// Update vertex positions for deformed meshes.
void UpdateVertexPosition(const MeshDataFromFileClass * StaticMeshData, const MyVector4Class * pVertices) const;
// Bind buffers, set vertex arrays, turn on lighting and texture.
void BeginDraw(ShadingMode pShadingMode) const;
// Draw all the faces with specific material with given shading mode.
void Draw(int pMaterialIndex, ShadingMode pShadingMode) const;
// Unbind buffers, reset vertex arrays, turn off lighting and texture.
void EndDraw() const;
// Get the count of material groups
int GetSubMeshCount() const return mSubMeshes.GetCount();
private:
enum
VERTEX_VBO,
NORMAL_VBO,
UV_VBO,
INDEX_VBO,
VBO_COUNT,
;
// For every material, record the offsets in every VBO and triangle counts
struct SubMesh
SubMesh() : IndexOffset(0), TriangleCount(0)
int IndexOffset;
int TriangleCount;
;
GLuint mVBONames[VBO_COUNT];
MyMeshArray<SubMesh*> mSubMeshes;
bool mHasNormal;
bool mHasUV;
bool mAllByControlPoint; // Save data in VBO by control point or by polygon vertex.
;
这是我的初始化函数:
bool Initialize(const MeshDataFromFileClass * StaticMeshData)
[...]
/*
Earlier code that retrieves data from file removed.
Only the point where the data is transferred to the GPU is shown.
*/
// Create VBOs
glGenBuffers(VBO_COUNT, mVBONames);
// Save vertex attributes into GPU
glBindBuffer(GL_ARRAY_BUFFER, mVBONames[VERTEX_VBO]);
glBufferData(GL_ARRAY_BUFFER, lPolygonVertexCount * VERTEX_STRIDE * sizeof(float), lVertices, GL_STATIC_DRAW);
delete [] lVertices;
if (mHasNormal)
glBindBuffer(GL_ARRAY_BUFFER, mVBONames[NORMAL_VBO]);
glBufferData(GL_ARRAY_BUFFER, lPolygonVertexCount * NORMAL_STRIDE * sizeof(float), lNormals, GL_STATIC_DRAW);
delete [] lNormals;
if (mHasUV)
glBindBuffer(GL_ARRAY_BUFFER, mVBONames[UV_VBO]);
glBufferData(GL_ARRAY_BUFFER, lPolygonVertexCount * UV_STRIDE * sizeof(float), lUVs, GL_STATIC_DRAW);
delete [] lUVs;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mVBONames[INDEX_VBO]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, lPolygonCount * TRIANGLE_VERTEX_COUNT * sizeof(unsigned int), lIndices, GL_STATIC_DRAW);
delete [] lIndices;
这是我的 BeginDraw 函数:
void MyMeshData::BeginDraw(ShadingMode pShadingMode) const
glBindBuffer(GL_ARRAY_BUFFER, mVBONames[VERTEX_VBO]);
/*
glVertexPointer(VERTEX_STRIDE, GL_FLOAT, 0, 0);
glEnableClientState(GL_VERTEX_ARRAY);
*/
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, VERTEX_STRIDE, GL_FLOAT, GL_FALSE, 0, 0);
// Set normal array.
if (mHasNormal && pShadingMode == SHADING_MODE_SHADED)
glBindBuffer(GL_ARRAY_BUFFER, mVBONames[NORMAL_VBO]);
glNormalPointer(GL_FLOAT, 0, 0);
glEnableClientState(GL_NORMAL_ARRAY);
// Set UV array.
if (mHasUV && pShadingMode == SHADING_MODE_SHADED)
glBindBuffer(GL_ARRAY_BUFFER, mVBONames[UV_VBO]);
glTexCoordPointer(UV_STRIDE, GL_FLOAT, 0, 0);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mVBONames[INDEX_VBO]);
if (pShadingMode != SHADING_MODE_SHADED)
glColor4fv(DEFAULT_WIREFRAME_COLOR);
我的绘图功能 ...
void MyMeshData::Draw(int pMaterialIndex, ShadingMode pShadingMode) const
// Where to start.
GLsizei lOffset = mSubMeshes[pMaterialIndex]->IndexOffset * sizeof(unsigned int);
if ( pShadingMode == SHADING_MODE_SHADED)
const GLsizei lElementCount = mSubMeshes[pMaterialIndex]->TriangleCount * 3;
glDrawElements(GL_TRIANGLES, lElementCount, GL_UNSIGNED_INT, reinterpret_cast<const GLvoid *>(lOffset));
else
for (int lIndex = 0; lIndex < mSubMeshes[pMaterialIndex]->TriangleCount; ++lIndex)
glDrawElements(GL_LINE_LOOP, TRIANGLE_VERTEX_COUNT, GL_UNSIGNED_INT, reinterpret_cast<const GLvoid *>(lOffset));
lOffset += sizeof(unsigned int) * TRIANGLE_VERTEX_COUNT;
最后是我的结束绘制函数......
void VBOMesh::EndDraw() const
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
【问题讨论】:
您的代码几乎无法阅读。在某个地方, 被 html&lt;
和 &gt;
取代。我建议重新粘贴它,因为您的模板代码现在是乱码。
@AndonM.Coleman 谢谢;我什至没有注意到这一点。进行了修改。
【参考方案1】:
我想知道是否可以只调用 如果我的大部分时间,“glDrawElements”以某种方式对内存中的内容起作用 VBO 没有改变并跳过我的 Begin 和 end draw 函数,如图所示 下面。
opengl 中有一个功能可以做到这一点,称为Vertex Array Buffer (VAO)。此功能允许您将开始绘制中的内容保存到对象中(很像 VBO)绑定它,取消绑定它,节省您的时间,因此您不必每次都手动绑定所有缓冲区。我真的不记得什么时候支持它了,它是自 opengl3 以来的核心功能,我对此很确定,据我所知,甚至 OpenGL ES 2.0 也通过extension 支持它。
如果有一个神奇的 OpenGL 函数,我可以一次性调用它, OpenGL 可以渲染我所有未更改的缓冲区 ID,我可以简单地 专注于画出变化的和相机?
如果我理解正确的话,您需要像缓存渲染这样的东西,而不是每次您想要一个函数时手动调用 glDrawElements,您可以在其中输入所有缓冲区 id 并告诉它“渲染这些”。据我所知,与此最接近的是instanced rendering,但这也有其局限性。
尽管如此,我认为这里可能还有其他东西,因为 VBO 已经使您的渲染速度更快,而且 GPU 不喜欢小型模型,大型模型对 GPU 非常有用,因为它有机会使用其漂亮的功能,超级骗子缓存以及如何不使其快速,对于小型模型,没有机会,因为在缓存开始填满之前,模型已经被渲染。因此,如果这在您的情况下运行缓慢,则可能是其他原因,因为您正在做的事情几乎是 GPU 达到其最佳性能的理想选择,我建议运行类似 gDebugger 的东西来分析哪个函数或代码片段需要最多的时间,如果看起来没问题,然后尝试使用 GPU 调试器/分析器来查看花费最多的时间(例如 NVPerfKit 用于 nVidia)。
【讨论】:
ES 2.0 不支持 VAO。 ES 3.0 可以。 谢谢我已经编辑了,我忘了它只支持通过es 2.0中的扩展。 @DrakkLord:你确定你的意思是 instanced 渲染吗?我认为indirect rendering 在这里会更相关,尽管这两个概念并不真正适合问题中提出的数据结构。 nvidia 声称使用 VAO 比不使用它要慢一些(遗憾的是,这是真的,尽管差异很小)。另一方面,在 ES 上,VAO 通常要快得多。但无论如何,对顶点、法线和纹理坐标使用单独的 VBO 是不好的——这一切都应该合并到一个交错的 VBO 中。 @DrakkLord 谢谢我会试试这个。以上是关于OpenGL - 绘制存储在 VBO 中的大量信息的主要内容,如果未能解决你的问题,请参考以下文章