OpenGL: сфера замедляет частоту кадров

#python #performance #opengl #pyopengl #opengl-compat

#python #Производительность #opengl #pyopengl #opengl-compat

Вопрос:

У меня есть класс, который создает сферу на основе переданного количества стеков, количества секторов и радиуса. Проблема в том, что всякий раз, когда я рисую сферу, моя частота кадров падает с 57-60 кадров в секунду до 20 кадров в секунду.

Вот как я рисую свою сферу:

     def draw_edges(self):
        """Draws the sphere's edges"""
        glPushMatrix()
        glTranslate(self.position[0], self.position[1], self.position[2])
        glRotate(self.rotation[3],self.rotation[0],self.rotation[1],self.rotation[2])
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
        glBegin(GL_TRIANGLES)
        for edge in self.edges:
            for vertex in edge:
                glVertex3fv(self.vertices[vertex])
        glEnd()
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
        glPopMatrix()
  

Кто-нибудь знает, как я мог бы ускорить это?

Комментарии:

1. циклы for могут быть довольно медленными в Python. Может быть, есть способ заменить два цикла for.

2. Обратите внимание, что вы используете устаревший конвейер фиксированных функций. Хотя, очевидно, поддерживается в вашей системе, это, вероятно, далеко от оптимального. Вам следует подумать о переходе на альтернативу, основанную на программируемых шейдерах.

3. Или, по крайней мере, списки отображения или VBO.

Ответ №1:

Попробуйте избавиться от вложенных циклов. Смотрите спецификацию Vertex для современного способа рисования сеток с использованием объекта Vertex Buffer и объекта Vertex Array.


Другая (устаревшая) возможность заключается в использовании фиксированных атрибутов функции.

Спецификация профиля совместимости с API OpenGL 4.6; 10.3.3 Указание массивов для атрибутов фиксированной функции; страница 402

Команды

 void VertexPointer( int size, enum type, sizei stride, const void *pointer );
void NormalPointer( enum type, sizei stride, const void *pointer );
void ColorPointer( int size, enum type, sizei stride, const void *pointer );
[...]
  

укажите местоположение и организацию массивов для хранения координат вершин, нормалей, цветов, […] Отдельный массив включается или отключается вызовом одного из

 void EnableClientState( enum array );
void DisableClientState( enum array );
  

с массивом, установленным в VERTEX_ARRAY , NORMAL_ARRAY , COLOR_ARRAY , […], для массива vertex, normal, color, […] соответственно.


Создайте список с данными атрибута вершины в конструкторе класса:

 def __init__(self):
    # [...]
   
    self.vertexArray = []
    for edge in self.edges:
        for vertex in edge:
            self.vertexArray.append(self.vertices[vertex])
  

Используйте массив, чтобы указать вершины и нарисовать сетку:

 def draw_edges(self):
    """Draws the sphere's edges"""
    glPushMatrix()
    glTranslate(self.position[0], self.position[1], self.position[2])
    glRotate(self.rotation[3],self.rotation[0],self.rotation[1],self.rotation[2])
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
    
    glEnableClientState(GL_VERTEX_ARRAY)
    glVertexPointer(3, GL_FLOAT, 0, self.vertexArray)
    glDrawArrays(GL_TRIANGLES, 0, len(self.vertexArray))
    glDisableClientState(GL_VERTEX_ARRAY)
    
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
    glPopMatrix()
  

Это первый (небольшой) шаг к современному решению с VBO и VAO.


Дальнейшее повышение производительности может быть достигнуто за счет использования объекта Vertex Buffer:

 def __init__(self):
    # [...]

    self.vertexArray = []
    for edge in self.edges:
        for vertex in edge:
            self.vertexArray  = self.verticies[vertex] # <--- flat list

    array = (GLfloat * len(self.vertexArray))(*self.vertexArray)
    self.vbo = glGenBuffers(1)        
    glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
    glBufferData(GL_ARRAY_BUFFER, array, GL_STATIC_DRAW)
    glBindBuffer(GL_ARRAY_BUFFER, 0)
  
 def draw_edges(self):
    # [...]

    glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
    glVertexPointer(3, GL_FLOAT, 0, None)
    glBindBuffer(GL_ARRAY_BUFFER, 0)
    
    glEnableClientState(GL_VERTEX_ARRAY)
    glDrawArrays(GL_TRIANGLES, 0, len(self.vertexArray) // 3)
    glDisableClientState(GL_VERTEX_ARRAY)

    # [...]
  

Комментарии:

1. Спасибо! Этот метод удвоил мою частоту кадров до чуть более 40 кадров в секунду.

2. @User-92 Спасибо. Всегда пожалуйста. Дальнейшее улучшение может быть достигнуто за счет использования VBO. Я расширю ответ. Всего один момент, пожалуйста…

3. @User-92 Я расширил ответ.

4. Вау, это безумие! Теперь у меня более 70 кадров в секунду. Я не знал, что VBO могут так сильно ускорить производительность. Большое вам спасибо!

5. @User-92 Фокус в том, что массив вершин загружается в графический процессор один раз, в конструкторе. Когда вы рисуете объект, нужно ссылаться только на массив. self.vbo это просто идентификатор имени.