2014-12-22 2 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, но я не могу найти пример, который показывает, что мне нужно делать.


Edit 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 и использование QT QGLWidget через PySide 1.2.2 amd64.

Вот дамп от 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 (возможно, несколько блоков VBOs, когда вы оптимизируете) со всеми необходимыми данными вершин. Затем вы индексируете в VBO, чтобы рисовать каждый примитив. Таким образом, ваш glDrawArrays начнет использовать второй параметр «первым».

Также нет необходимости включать и отключать ClientState каждый раз в цикле, это может быть причиной водителю выполнять ненужную проверку

Пожалуйста, простите мое отсутствие питона-ГЛ знаний, но это будет псевдо выглядеть как это.

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 благодаря вашему предложению - я отправлю рабочий пример. Частота кадров намного хуже, чем при использовании Display Lists. – lemondifficult

+0

Вы сказали, что используете VBO, но GL_VERTEX_ARRAY и glEnableClientState с помощью указателя (v) указывают, что вы этого не сделали. На самом деле то, что я предложил, полностью нарушено, если вы просто используете массивы клиентских вершин. Это плохо, потому что драйвер должен копировать все ваши данные клиента каждый раз, когда вы рисуете. VBO только копирует данные вершин один раз, и с этого момента он находится в памяти GPU. см .: http://www.songho.ca/opengl/gl_vertexarray.html Пробуйте информацию о vbo в этой ссылке тоже – djgandy

+0

ОК, я смущен - в этой ссылке у него есть еще одна ссылка на его страницу VBO, здесь: [link] (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 находится в GL с версии 3.1, поэтому разумно использовать его в настоящее время.

  2. В качестве дополнения к ответу datenwolf: используйте GL_ARB_multi_draw_indirect (в OpenGL с 4.3). Разница с glMultiDrawArrays заключается в том, что также массив параметров рисования хранится в буферном объекте, так что вызов рисования больше не зависит от клиентской памяти, что является более эффективным. Поскольку эта функция доступна только на довольно современных графических процессорах, вы, вероятно, захотите по-прежнему использовать glMultiDrawArrays и необязательно предоставить код для косвенного варианта, если он доступен.

+0

Согласовано. Довольно много переделок, чтобы добраться до 4.3 функций. Также мне бы не хотелось советовать использовать профиль совместимости даже для доказательства концепции. Если вы можете использовать чистые версии 4.3, то идите! – djgandy

+0

Спасибо, я просто придерживаюсь glMultiDrawArrays, возможно, в будущем может посмотреть Primitive 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() 

И это все. Он работает, но частота кадров намного хуже, чем пример Display List. Для справки я получаю 4.4FPS, делая это таким образом, по сравнению с примерно 48FPS с помощью Display List - очевидно, что это очень зависит от аппаратного обеспечения, но относительная разница огромна (это просто используется на борту Intel HD Graphics 3000).

+0

Хорошо. Очень странно. Список отображения будет оптимальным, потому что он будет скомпилировать все состояния, но не должно быть трудно достичь аналогичной производительности с помощью vbos. Если вы можете профилировать, было бы полезно увидеть, где узкое место. 4.4fps очень низкое. – djgandy

+0

Никогда раньше не использовался профилировщик OpenGl, но я только что установил gDEBugger. Не совсем уверен в том, что я ищу, но в «панели показателей производительности» на снимке у меня есть: 'Средство использования центральных процессоров: 29 GL Кадры/сек: 4 GL Время кадра: 284 OGL calls/frame: 1 ' – lemondifficult

+0

Как ни странно, когда я перехожу в« Textures, Buffers and Images Viewer », я вижу буферы глубины, трафарета, фронта и спины, но для объектов буфера VBO он говорит:« Нет доступных VBOs »- не знаю, если это всего лишь предел инструмента, я использую его неправильно или действительно нет VBO? – lemondifficult

Смежные вопросы