绘制多边形网格时的 Opengl 性能问题

Posted

技术标签:

【中文标题】绘制多边形网格时的 Opengl 性能问题【英文标题】:Opengl performance issue when drawing polygon meshes 【发布时间】:2012-10-15 11:12:07 【问题描述】:

我正在使用以下代码在 3D 游戏中绘制一些多边形网格。

void drawModelFace(const MeshFace *face, float *vertices, float *vertNormals, float *textureVerts)
   
    glBegin(GL_POLYGON);
    for (int i = 0; i < face->_numVertices; i++) 
    
       glNormal3fv(&vertNormals[3 * face->_vertices[i]]);

       if (face->_texVertices)
       
           glTexCoord2fv(&textureVerts[2 * face->_texVertices[i]]);
       

       glVertex3fv(&vertices[3 * face->_vertices[i]]);
    
    glEnd();

我的问题是当这个函数被多次调用时,我在游戏中遇到了一些性能问题。

此函数平均每秒调用 50000 次,提供恒定的 60fps,但在某些地方,它被称为每秒 100000 次,提供 15fps。 (我用今天的电脑降频到1Ghz来模拟今天手机的性能)

我听说即时模式可能很慢,这就是我尝试使用 glDrawArrays 的原因。代码如下:

void drawModelFace(const MeshFace *face, float *vertices, float *vertNormals, float *textureVerts)
   
    GLfloat vert[3*face->_numVertices];
    GLfloat normal[3*face->_numVertices];
    GLfloat tex[2*face->_numVertices];

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);
    glVertexPointer(3, GL_FLOAT, 0, vert);
    glTexCoordPointer(2, GL_FLOAT, 0, tex);
    glNormalPointer(GL_FLOAT, 0, normal);

    for (int i = 0; i < face->_numVertices; i++) 
    
        vert[0 + (i*3)] = vertices[3 * face->_vertices[i]];
        vert[1 + (i*3)] = vertices[3 * face->_vertices[i]+1];
        vert[2 + (i*3)] = vertices[3 * face->_vertices[i]+2];

        normal[0 + (i*3)] = vertNormals[3 * face->_vertices[i]];
        normal[1 + (i*3)] = vertNormals[3 * face->_vertices[i]+1];
        normal[2 + (i*3)] = vertNormals[3 * face->_vertices[i]+2];

            if (face->_texVertices)
            
                tex[0 + (i*2)] = textureVerts[2 * face->_texVertices[i]];
                tex[1 + (i*2)] = textureVerts[2 * face->_texVertices[i]+1];
            
    

    glDrawArrays(GL_TRIANGLE_FAN ,0, face->_numVertices);
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY); 

但性能结果完全一样。

如何优化我的代码以获得一些 fps?

请注意,我的最终目标是将此代码用于 android 设备,因此不再允许 glBegin 和 glEnd。

【问题讨论】:

您似乎正在研究如何在实际绘图功能中而不是事先绘制网格。我的意思是你的绘图函数应该简单地传递已经构建的数组。所以这里的问题更多的是你如何管理你的模型工作流程而不是其他任何事情。我会确保每个网格都有一个 VBO 和一个索引缓冲区。这样您就不必在绘图中间围绕网格进行迭代。你只需在显卡上扔一堆三角形和索引。 在 ES 1.1 中,您的选择非常有限。它是 glDrawArrays 和 glDrawElements,仅此而已。此外,PC 架构与嵌入式设备有很大不同。具有 x16 PCIe 总线和 NVIDIA 主板的低频 CPU 不太可能提供与 PowerRV 相同的性能。 【参考方案1】:

我认为 glDrawArray 可能是最好的选择。如果我没记错的话,数组中的数据将在每次迭代中从客户端发送到服务器。如果数据在每次迭代中都发生了变化,那么这并不是一个真正的问题,因为客户端需要在每次数据发生变化时将数据发送到服务器。这意味着由于 VBO 的实施,在服务器内存上存储大量数据不会真正给您带来任何性能提升,因为无论如何您都必须重新发送这些数据。

您使用的是大型对象还是许多小型对象? 我相当有信心 glDrawArrays 在大型对象的情况下是最佳的。

“性能结果完全相同”究竟是什么意思?是非常相似还是有什么不同?如果性能完全相同,我听起来有点怀疑。

【讨论】:

仅供参考,此答案中的后续问题最好作为原始问题的 cmets 发布(而不是在您的答案中)。 我使用了很多小对象,因为每次调用函数时只有 3 或 4 个顶点。 使用 gldrawarrays 带来的性能提升会因为有很多对象而被减轻。 gldrawarrays 的最大优势是客户端可以非常快速地向服务器发送非常大量的数据,然后继续执行下一行代码,让服务器处理它刚刚收到的大量数据。这反对使用 glBegin/glEnd 客户端需要在每个 glvertex 调用之间等待(至少这是我的理解,也许我错了)。 因此使用许多小对象仍然会导致客户端阻塞,因为它需要多次发送数组,而不是一次发送一个大数组。经常更改许多小对象或多或少是最糟糕的情况性能明智的。您可能需要考虑替代解决方案,在这些解决方案中,您可以在应用程序中将数据按逻辑分组在一起。我认为使用 VBO 和许多小对象也是一个坏主意。 另外,您可能需要重新考虑如何更改顶点。如果您可以更改组中顶点的位置,则可以通过使用例如 VBO 上的 glrotate/gltransform/glscale 调用来获得性能,而不是更改 gldrawarray 中的值并在每次迭代中发送它【参考方案2】:

如果您的网格没有改变(即它是一个静态模型),那么您可以使用 display list

这允许您将所有顶点/纹理/法线调用预先组合到一个列表中,然后通过对 glCallList 的单个函数调用来呈现该列表。

【讨论】:

不幸的是它不是一个静态模型,而且显示列表不是我的最终目标 opengl-es 1.1 的一部分。有人告诉我顶点缓冲区对象,但我不知道如何在我的代码中实现它。

以上是关于绘制多边形网格时的 Opengl 性能问题的主要内容,如果未能解决你的问题,请参考以下文章

如何提高使用 SkiaSharp 绘制大多边形的性能?

二维平面三角划分

Android OpenGL 是不是具有用于绘制位图性能的离屏功能

在 OpenGL 中渲染网格多边形 - 非常慢

在 Qt C++ 中的自定义项委托上绘制文本时的性能问题

SDL:性能 SPriG 与 SDL_gfx