安卓OpenGL ES中,使用glDrawArrays还是glDrawElements?

22
如果我使用glDrawArraysglDrawElements,哪种方法最好?它们有什么区别吗?

被接受的答案略有过时。根据Jorn Horstmann的回答中的文档链接,苹果描述了如何使用“退化三角形”技巧与DrawElements一起使用,从而获得最佳效果。通过将所有数据组合成单个GL调用DrawElements,所获得的节省指数不值得使用DrawArrays节省的时间(如果使用DrawArrays组合所有内容,则任何“浪费的元素”都会浪费顶点,这些顶点比索引大得多,并且需要更多的渲染时间)。最佳方案:使用GL_TRIANGLE_STRIP进行单个DrawElements调用。 - ToolmakerSteve
4个回答

34

对于两者,您都需要向OpenGL传递包含顶点数据的缓冲区。

glDrawArrays基本上是“使用我之前提供的数据,绘制这个连续范围的顶点”。

  • 您不需要构建索引缓冲区。
  • 如果将数据组织成GL_TRIANGLES,则相邻三角形会有重复的顶点数据。这显然是浪费的。
  • 如果使用GL_TRIANGLE_STRIP和GL_TRIANGLE_FAN来避免重复数据:它并不是非常有效,而且您必须为每个条带和扇形呈现调用。OpenGL调用很昂贵,应尽可能避免使用。

使用glDrawElements,您会传递一个包含要绘制顶点索引的缓冲区。

  • 没有重复的顶点数据-您只需为不同的三角形索引相同的数据。
  • 您可以直接使用GL_TRIANGLES,并依赖顶点缓存来避免处理相同的数据-无需重新组织几何数据或在多个呈现调用中拆分渲染。
  • 索引缓冲区的内存开销。

我的建议是使用glDrawElements。


不确定对于具有大量静态顶点的东西(如地形)是否适用-索引的开销似乎太大了。然而,使用drawrangeelements的好处在于渲染灵活性上非常理想,似乎它的优点会超过任何缺点。这只是一个想法。 - scape
@scape - 我认为即使顶点是静态的,因为索引比顶点数据本身要小得多,发送所有这些索引并不是什么大问题。我建议使用DrawElements以获得灵活性。(或者使用VBOs,在其中包含一个索引缓冲区来实现相同的效果)。 - ToolmakerSteve
@ryanm - 如果有人有使用DrawArrays的理由,那么有一个广泛描述的技巧可以将条带组合在一起,以便在单个调用中发送它们:复制一个条带的最后一个顶点和下一个条带的第一个顶点。结果是两个退化三角形,其面积为零。我只是提到这一点,以防止DrawArrays不可避免地效率低下的错误信息。但就个人而言,与使用索引相比,这太麻烦了,收益微薄... 更新 我刚看到Jorn Horstmann在他的答案中提到了这一点。 - ToolmakerSteve
更新 #2:刚刚阅读了Jorn Horstmann答案中的文档链接。使用DrawElements合并三角形条带也很好,因此没有必要使用DrawArray。正如文档所说,最重要的是尽量减少gl调用的数量,因此将所有数据组合成单个三角形条带。这使您可以在单个调用中混合“高效”的三角形条带和任意几何图形。 - ToolmakerSteve

6
性能影响在 iPhone 上可能相似,iOS 的 OpenGL ES 编程指南建议使用三角形条带并通过退化三角形连接多个条带。
该链接有一个很好的概念图。这样你可以重用一些顶点,并仍然在一步中完成所有绘制。
最佳性能要求将模型作为单个未索引的三角形条带使用 glDrawArrays 提交,尽可能少地重复顶点。如果您的模型需要重复许多顶点(因为许多顶点由不按顺序出现在三角形条带中的三角形共享或因为您的应用程序合并了许多较小的三角形条带),则可以使用单独的索引缓冲区并调用 glDrawElements 来获得更好的性能。存在权衡:未索引的三角形条带必须定期复制整个顶点,而索引的三角形列表需要附加内存以保存索引,并增加查找顶点的开销。为了获得最佳结果,请使用索引和未索引的三角形条带测试您的模型,并使用执行最快的那个。
在可能的情况下,对顶点和索引数据进行排序,以使共享公共顶点的三角形在三角形条带中绘制得相当接近。图形硬件经常缓存最近的顶点计算,因此参考位置可能允许硬件避免多次计算一个顶点。
缺点是你可能需要一个预处理步骤来对网格进行排序,以获得足够长的条带。我还没有想出一个好的算法,因此无法提供与 GL_TRIANGLES 相比的性能或空间数字。当然,这也高度依赖于您要绘制的网格。

注意:该文档的当前版本不再提及DrawArrays。相反,它们展示了使用带有Degenerate Triangles技巧的DrawElements。这样可以兼顾两种方法的优点:可以处理任意模型(稍微增加一些开销,但是开销是额外的索引,而不是额外的顶点数据,因此很小),并且在三角形条带中最小化数据以获得优化。 - ToolmakerSteve

1

已被接受的答案略有过时。在Jorn Horstmann的回答中跟随文档链接OpenGL ES Programming Guide for iOS,苹果描述了如何使用"退化三角形技巧"与DrawElements一起使用,从而获得最佳效果。

通过使用DrawArrays节省一些索引的小优惠并不值得你通过将所有数据组合成单个GL调用DrawElements来获得节省。 (您可以使用DrawArrays将所有内容组合在一起,但是任何“浪费的元素”都会浪费顶点,这些顶点比索引大得多,并且需要更多的渲染时间。)

这也意味着您无需仔细考虑所有模型,以确定它们中的大多数是否可以渲染为最少数量的条带,或者它们是否太复杂。一个统一的解决方案,处理所有事情。(但请尽可能地组织条带,以最小化发送的数据,并最大程度地增加GPU重用最近缓存的顶点计算的可能性。)

BEST: 一次使用 GL_TRIANGLE_STRIP 的 DrawElements 调用,包含所有每帧更改的数据。


1

实际上,您可以退化三角形带,以创建连续的带,这样您就不必在使用glDrawArray时将其拆分。

我一直在使用glDrawElements和GL_TRIANGLES,但考虑改用glDrawArray与GL_TRIANGLE_STRIP。这样就不需要创建索引向量。

是否有人更了解在帖子中提到的顶点高速缓存事物?考虑glDrawElements/GL_TRIANGLE与glDrawArray/GL_TRIANGLE_STRIP之间的性能差异。


请查看Jorn Horstmann的回答中的文档链接以及我的评论。不要尝试消除索引缓冲区。使用DrawElements的三角形带加退化三角形技术。具有索引缓冲区的额外成本很小,这使您可以将所有几何体组合成单个调用。没有浪费的顶点,这些顶点是昂贵的。不必权衡良好分离的几何体和不形成良好分离的任意模型之间的折衷。 - ToolmakerSteve

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接