2014-12-22 44 views
1

我有一個簡單的OpenGL應用程序,我正在繪製海岸線數據。數據是多邊形的長列表(約41000),其中每個多邊形是(x,y)點的列表。多邊形都是不同的長度(因爲一些海岸線比其他海岸線長)。 現在,使用顯示列表,這是微不足道的畫 - 這是Python代碼,但我敢肯定,你得到的要點:VBO與多個圖元顯示列表等價嗎?

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中,只需要一個綁定和一個glDrawArrays?我不明白的是,glDrawArrays如何知道每個多邊形中有多少個點,因爲每個多邊形都有不同點?如果這是非常微不足道的,但我一直在閱讀,我可以用VBOs完成任何顯示列表,但是我找不到一個顯示我需要做什麼的示例。


編輯1 - 慢VBO性能的光,下面是我的平臺的詳細信息

的Windows 7 64位
i5-2430M
GPU只是板載Intel高清顯卡3000
4GB內存

應用程序是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 
+0

你的目標是什麼?最先進的圖形芯片?或者過去5年以上製造的任何東西?正如另一個答案中所述,在較新的gl版本中有更好的功能,但是您將被限制在較新的nvidia和amd卡中。 – djgandy

+0

更像我在過去5年以上所做的任何事情。像3.1版本一樣? – lemondifficult

回答

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() 

這仍然將是次優太,因爲這將需要大量drawcalls,並drawcalls可以是相當昂貴的。但從你的例子來看,它應該是一個簡單的漸進式改進。之後,您可以嘗試glMultiDrawArrays,因爲您只有一種基本類型,所以應該使您能夠完全移除循環。

+0

謝謝,我現在使用glMultiDrawArrays感謝您的建議 - 我將發佈一個工作示例。雖然幀率比使用顯示列表時差得多。 – lemondifficult

+0

你說你使用的是VBO,但是使用(v)指針的GL_VERTEX_ARRAY和glEnableClientState建議你不是。事實上,如果你只是使用客戶端頂點數組,我建議完全破壞。這很糟糕,因爲驅動程序必須在每次繪製時複製所有客戶端數據。一個VBO只複製一次頂點數據,並從此開始在GPU內存中。 見:http://www.songho.ca/opengl/gl_vertexarray.html 嘗試在這個環節的VBO信息太 – djgandy

+0

OK,我很困惑 - 在鏈路他還有另外一個鏈接到他的VBO頁面,在這裏: [鏈接](http://www.songho.ca/opengl/gl_vbo.html),並在那裏'繪圖VBO'部分他這樣做: 'glBindBufferARB(GL_ARRAY_BUFFER_ARB,vboId1); glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB,vboId2); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3,GL_FLOAT,0,0); glDrawElements(GL_QUADS,24,GL_UNSIGNED_BYTE,0); glDisableClientState(GL_VERTEX_ARRAY); glBindBufferARB(GL_ARRAY_BUFFER_ARB,0); glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB,0);' 這不就像我在做什麼? – lemondifficult

2

glDrawArrays需要一個偏移量和一個頂點數的參數。你看到這是怎麼回事?然後有glMultiDrawArrays批量大量繪圖批次。

所以這裏是你要做的:把你所有的海岸線數據放到一個大的VBO中。對於每個多邊形/線條循環確定偏移量和頂點數並將它們附加到數組。最後,綁定VBO並調用glMultiDrawArrays傳遞offset/vertexcount數組作爲參數,並傳遞數組中元素的數量(這將是您的41000數字)。

2

您還沒有exolicitely說哪個版本的OpenGL你的目標,所以我會建議更現代的替代品已經發布至今的答案:

  1. 使用primitive restart功能: 你很好需要對其進行索引渲染,並且只需定義一個特殊索引即可結束當前線條並開始新索引。使用這種技術,您可以用一次繪製調用繪製整個數據。但是,您將需要一個您以前不需要的索引緩衝區。而且你不可能在你的場景中共享頂點,所以你不會通過使用它獲得更多的收益。 Primtive Restart從版本3.1開始在GL中,因此現在使用它是合理的。

  2. 作爲datenwolf的答案的擴展:使用GL_ARB_multi_draw_indirect(自4.3開始,在OpenGL中)。與glMultiDrawArrays的不同之處在於繪製參數數組也保存在緩衝區對象中,以便繪製調用不再依賴於客戶端內存,這更有效。由於此功能僅適用於相當先進的GPU,因此您可能仍想使用glMultiDrawArrays,並且可選地爲間接變體提供代碼路徑(如果可用)。

+0

同意。儘管如此,爲了獲得4.3的功能還是有很多改進。此外,我不建議使用兼容性配置文件,即使對於概念證明。如果你可以使用純粹的4.3,儘管去吧! – djgandy

+0

謝謝,我只是堅持使用glMultiDrawArrays,可能會在未來看看Primtive Restart。 – lemondifficult

0

OK,一旦我知道搜索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使用顯示列表 - 顯然這是非常依賴硬件,但相對差異是巨大的(這只是使用板載英特爾高清顯卡3000)。

+0

好的。很奇怪。顯示列表將是最優的,因爲它會編譯所有狀態,但使用vbos實現類似的性能不應該很難。如果你能分析一下,看看瓶頸在哪裏將是有用的。 4.4fps非常低。 – djgandy

+0

以前從未使用OpenGl分析器,但我剛安裝了gDEBugger。不太清楚我在找什麼,但是在快照的「性能儀表板」中,我有: 'CPUs平均利用率:29 GL幀/秒:4 GL幀時間:284 OGL調用/幀:1 ' – lemondifficult

+0

奇怪的是,當我進入'紋理,緩衝區和圖像查看器'時,我可以看到深度,模板,前部和後部緩衝區,但是對於VBO緩衝區對象,它表示「沒有可用的VBO」 - 不知道是否這只是工具的限制,我使用它錯了,或者確實沒有VBO? – lemondifficult