Python VBO等价于具有多个原语的显示列表?

Python VBO等价于具有多个原语的显示列表?,python,opengl,vbo,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_p

我有一个简单的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)
这表现得很好

我的问题是:如何使用VBOs实现等效功能?我最初的方法是为每个多边形创建一个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中,只做一个绑定和一个数组吗?我不明白的是GLDrawArray如何知道每个多边形中有多少个点,因为每个多边形的点都不同

如果这是非常琐碎的,我很抱歉,但我一直在读,我可以用显示列表做的任何事情都可以用VBOs做,但我找不到一个例子来说明我需要做什么


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

Windows 7 64位
i5-2430M
GPU仅搭载Intel HD Graphics 3000
4GB内存

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

这是格德布格的垃圾堆:

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

您可能不应该为每个多边形创建VBO。这将非常缓慢,并且不是驱动程序在您的第一个实现中在幕后所做的。您应该有一个VBO(在优化时可能有几个分块的VBO),其中包含所需的所有顶点数据。然后索引到VBO中以绘制每个基本体。因此,您的GLDrawArray将开始使用第二个参数“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()

这也将是次优的,因为它将需要大量的DrawCall,而且DrawCall可能会相当昂贵。但这应该是从您的示例中得到的一个简单的增量改进。之后,您可以尝试
glMultiDrawArrays
,这将使您能够完全删除循环,因为您只有一个基元类型

您可能不应该为每个多边形创建VBO。这将非常缓慢,并且不是驱动程序在您的第一个实现中在幕后所做的。您应该有一个VBO(在优化时可能有几个分块的VBO),其中包含所需的所有顶点数据。然后索引到VBO中以绘制每个基本体。因此,您的GLDrawArray将开始使用第二个参数“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()
这也将是次优的,因为它将需要大量的DrawCall,而且DrawCall可能会相当昂贵。但这应该是从您的示例中得到的一个简单的增量改进。之后,您可以尝试
glMultiDrawArrays
,这将使您能够完全删除循环,因为您只有一个基元类型

gldrawArray采用偏移量和许多顶点参数。你知道这是怎么回事吗?然后需要批量处理大量的绘图批次

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

glDrawArrays接受偏移量和一些顶点参数。你知道这是怎么回事吗?然后需要批量处理大量的绘图批次


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

您没有明确说明要针对哪个OpenGL版本,因此,我将提出比目前已经发布的答案更现代的替代方案:

  • 使用该功能: 为此,您需要索引渲染,并且只需定义 一个特殊的索引,它将结束当前行带并开始一个新的行带。使用此技术,您可以通过一次绘制调用绘制整个数据。但是,您需要一个以前不需要的索引缓冲区。而且,在您的场景中,您不太可能共享虚拟场景,因此使用它不会获得太多收益。Primitive Restart从3.1版开始就在GL中,因此现在使用它是合理的

  • 作为DatenWalf答案的扩展:use(从4.3开始在OpenGL中)。与
    glMultiDrawArrays
    的区别在于,draw参数的数组也保存在缓冲区对象中,因此draw调用不再依赖于客户机内存,这更有效。由于此功能仅在非常现代的GPU上可用,因此您可能仍希望使用
    glMultiDrawArrays
    ,并且如果间接变量可用,还可以选择为其提供代码路径


  • 你没有说得太过分了
    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()