具有多个基元的VBO等效显示列表?

时间:2014-12-22 17:15:28

标签: python opengl vbo

我有一个简单的OpenGL应用程序,我正在绘制海岸线数据。数据是一长串多边形(约41000),其中每个多边形是(x,y)点的列表。多边形的长度都不同(因为一些海岸线比其他海岸线长)。 现在,使用显示列表这是很容易绘制的 - 这是Python代码,但我确信你得到了要点:

self.coastlines_dl = GL.glGenLists(1)
GL.glNewList(self.coastlines_dl, GL.GL_COMPILE)
for poly in coastline_polys:
    GL.glBegin(GL.GL_LINE_STRIP)
        for point in poly:
            GL.glVertex3f(point[0], point[1], 0.0)
    GL.glEnd()
GL.glEndList()

然后在渲染循环中:

GL.glCallList(self.coastlines_dl)

表现非常好。

我的问题是:如何使用VBO进行等效操作?我最初的方法,因为我以前从未使用过VBO而只是想让它工作,就是为每个多边形创建一个VBO,然后在渲染循环中绘制每个VBO,如:

for v in self.vbos:
    v.bind()
    GL.glEnableClientState(GL.GL_VERTEX_ARRAY)
    GL.glVertexPointerf(v)
    GL.glDrawArrays(GL.GL_LINE_STRIP, 0, v.data.shape[0])
    GL.glDisableClientState(GL.GL_VERTEX_ARRAY)
    v.unbind()

这是有效的,因为它绘制数据,但帧速率令人震惊,这并不奇怪,因为我绑定并每帧绘制41000+次。

那么我可以把所有的多边形放到一个大的VBO中,只做一个绑定和一个glDrawArrays吗?我不明白的是glDrawArrays如何知道每个多边形中有多少个点,因为每个多边形都有不同的点?

道歉,如果这是非常微不足道的,但我一直在阅读我可以用VBO完成我可以用显示列表做的任何事情,但我找不到一个显示我需要做的事情的例子。


编辑1 - 鉴于缓慢的VBO性能,这是我的平台详细信息

Windows 7 64位
i5-2430M
GPU只是板载Intel HD Graphics 3000 4GB RAM

应用程序是Python OpenGL。 Python版本为2.7.9 64位,PyOpenGL 3.1.0 amd64,并通过PySide 1.2.2 amd64使用QT QGLWidget。

这是来自gDEBugger的转储:

General
-------
OpenGL Version           3.1
Shading Language Version 1.40  - Intel Build 8.15.10.2345
Is Backward-Compatible Yes
Is Forward-Compatible No
Is Debug Context Yes
Renderer Vendor Intel
Renderer Name Intel(R) HD Graphics Family
Renderer Version 3.1.0 - Build 8.15.10.2345
Renderer Type Installable client
Marked for Deletion      No
OpenGL Sharing Contexts  None

Pixel Format Information
------------------------
Pixel Format ID          9
Hardware Acceleration    Full
Double-Buffered          Yes
Stereographic            No
Native Rendering         No

Channel                  Bits
Red                      8
Green                    8
Blue                     8
Alpha                    8
Depth                    24
Stencil                  8
Accumulation             64

4 个答案:

答案 0 :(得分:3)

您可能不应该为每个多边形制作VBO。这将是非常缓慢的,并不是驱动程序将在您的第一个实现中幕后做的事情。您应该拥有一个VBO(在优化时可能只有几个VBO),并且您需要所有顶点数据。然后,您可以索引到VBO以绘制每个基元。所以你的glDrawArrays将开始使用第二个参数'first'。

此外,无需在循环中每次启用和禁用ClientState,这样做可能会导致驱动程序执行不必要的验证

请原谅我缺乏python-gl知识,但伪看起来像这样。

v.bind()
GL.glEnableClientState(GL.GL_VERTEX_ARRAY)
GL.glVertexPointerf(v)
for v in self.vbos:
    GL.glDrawArrays(GL.GL_LINE_STRIP, vbo_offset_goes_here, v.data.shape[0])
GL.glDisableClientState(GL.GL_VERTEX_ARRAY)
v.unbind()

这仍然不是最理想的,因为它需要大量的抽奖,并且抽奖可能相当昂贵。但它应该是一个简单的增量改进,从你的例子。之后,您可以尝试使用glMultiDrawArrays,这样您就可以完全删除循环,因为您只有一个基本类型。

答案 1 :(得分:2)

glDrawArrays 采用偏移量和顶点数量参数。你知道这是怎么回事吗?然后有glMultiDrawArrays来批量处理大量的图纸批次。

所以这就是你要做的:把你所有的海岸线数据放到一个大的VBO中。对于每个多边形/线环,确定偏移量和顶点数并将它们附加到数组。最后绑定VBO并调用glMultiDrawArrays,将offset / vertexcount数组作为参数传递,并传递数组中的元素数(这将是你的41000个数字)。

答案 2 :(得分:2)

您尚未明确说明您要定位的OpenGL版本,因此我将建议使用目前为止已发布的答案的更现代的替代方案:

  1. 使用primitive restart功能: 您需要为其编制索引,并且只需定义 一个特殊的索引,它将结束当前的线条并开始一个新的线条。使用此技术,您可以使用单个绘制调用绘制整个数据。但是,您需要一个以前不需要的索引缓冲区。并且您不太可能在场景中共享顶点,因此使用它不会获得太多收益。 Primtive Restart从版本3.1开始就在GL中,因此现在使用它是合理的。

  2. 作为datenwolf答案的延伸:使用GL_ARB_multi_draw_indirect(自4.3以来的OpenGL)。与glMultiDrawArrays的区别在于绘制参数数组也保存在缓冲区对象中,因此绘制调用不再依赖于客户端内存,这样更有效。由于此功能仅适用于相当现代的GPU,因此您可能仍希望使用glMultiDrawArrays,并可选择为间接变体提供代码路径(如果可用)。

答案 3 :(得分:0)

好的,一旦我知道根据这里的建议搜索glMultiDrawArrays,我 发现了这个:

Python glMultiDrawArrays / VBO example

或多或少做我想做的事。

以下是我现在正在做的事情:首先,设置VBO:

# In this example 'self.polys' is a list of polygons, where each polygon is a
# list of tuples, so something like:
# [[(30.0, 30.0, 0.0), (30.0, -30.0, 0.0), (0.0, -30.0, 0.0),
#   (0.0, 30.0, 0.0), (30.0, 30.0, 0.0)],
#  [(-30.0, 20.0, 0.0), (-25.0, 0.0, 0.0), (-35.0, 0.0, 0.0),
#   (-30.0, 20.0, 0.0)],
#  [(-40.0, -40.0, 0.0), (-30.0, -40.0, 0.0), (-30.0, -50.0, 0.0),
#   (-40.0, -40.0, 0.0)]]

# self.counts holds an integer for each polygon, giving its length, so
# [5, 4, 4] to continue our example
self.counts = [len(poly) for poly in self.polys]

# Creates an array of cumulative values of each element in self.counts,
# so array([5, 9, 13]) to continue our example:
tmp_first = numpy.array(self.counts).cumsum()
# Turn this into a list of the indexes of the start of each poly. The first
# one starts at zero, obviously, and in our example the second one starts
# at 5, and the second one at 9, so in our example self.first here ends up
# as [0, 5, 9]:
self.first = list(numpy.hstack((numpy.array([0]), tmp_first[:-1])))

# Now 'flatten' our polygon list of lists into a single list of points, so
# that to continue our example we end up with something like:
# [(30.0, 30.0, 0.0), (30.0, -30.0, 0.0), (0.0, -30.0, 0.0),
#  (0.0, 30.0, 0.0), (30.0, 30.0, 0.0), (-30.0, 20.0, 0.0),
#  (-25.0, 0.0, 0.0), (-35.0, 0.0, 0.0), (-30.0, 20.0, 0.0),
#  (-40.0, -40.0, 0.0), (-30.0, -40.0, 0.0), (-30.0, -50.0, 0.0),
#  (-40.0, -40.0, 0.0)]
polys_unwrapped = [item for sublist in polys for item in sublist]
# Convert this flattened list into a numpy array, and use to create our VBO:
self.single_vbo = vbo.VBO(numpy.array(polys_unwrapped, dtype=numpy.float32),
                          usage='GL_STATIC_DRAW')

然后在渲染循环中我这样做:

self.single_vbo.bind()
GL.glEnableClientState(GL.GL_VERTEX_ARRAY)
GL.glVertexPointerf(self.single_vbo)
GL.glMultiDrawArrays(GL.GL_LINE_STRIP, self.first, self.counts,
                     len(self.counts))
GL.glDisableClientState(GL.GL_VERTEX_ARRAY)
self.single_vbo.unbind()

就是这样。它工作正常,但帧率比显示列表示例差得多。作为参考,我用这种方式得到大约4.4FPS,相比之下使用显示列表大约48FPS - 显然这与硬件有关,但相对差异很大(这只是使用板载Intel HD Graphics 3000)。