Search code examples
pythonperformanceopenglpyopenglopengl-compat

OpenGL: Sphere slowing down framerate


I have a class that makes a sphere based on the passed in stack count, sector count and radius. The issue is, whenever I draw the sphere, my frame-rate drops from 57-60 fps to 20 fps.

This is how I draw my sphere:

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

Does anyone know how I could speed that up?


Solution

  • Try to get rid of the nested loops. See Vertex Specification for a modern way of drawing meshes by the use of a Vertex Buffer Object and Vertex Array Object.


    Another (deprecated) possibility is to use fixed function attributes.

    OpenGL 4.6 API Compatibility Profile Specification; 10.3.3 Specifying Arrays for Fixed-Function Attributes; page 402

    The commands

    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 );
    [...]
    

    specify the location and organization of arrays to store vertex coordinates, normals, colors, [...] An individual array is enabled or disabled by calling one of

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

    with array set to VERTEX_ARRAY, NORMAL_ARRAY, COLOR_ARRAY, [...], for the vertex, normal, color, [...] array, respectively.


    Create a list with the vertex attribute data in the constructor of the class:

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

    Use the array to specify the vertices and to draw the mesh:

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

    This is the first (small) step to a modern solution with VBO and VAO.


    A further performance improvement can be gained by the use of a Vertex Buffer Object:

    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)
    
        # [...]