Pygame OpenGL 3D Cube Lag

无人久伴 提交于 2021-02-17 02:06:24

问题


I am following a tutorial series that is rather old on pyOpenGL and I am doing exactly as he does. However I am experiencing lag - I have AMD FX-6300 with 8gb ram, GTX-1050ti and files are stored on a flashdrive. I have read some places that using glBegin and glEnd cause issues? What should I use instead and how would I do it in this code:

import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
import random

"""
- A Cube has 8 Nodes/Verticies
- 12 Lines/connections
- 6 Sides
"""


vertices = (
    (1, -1, -1),
    (1, 1, -1),
    (-1, 1, -1),
    (-1, -1, -1),
    (1, -1, 1),
    (1, 1, 1),
    (-1, -1, 1),
    (-1, 1, 1)
)

edges = ( #Contains vertexes/nodes
    (0, 1),
    (0, 3),
    (0, 4),
    (2, 1),
    (2, 3),
    (2, 7),
    (6, 3),
    (6, 4),
    (6, 7),
    (5, 1),
    (5, 4),
    (5, 7)
)

surfaces = (
    (0,1,2,3),
    (3,2,7,6),
    (6,7,5,4),
    (4,5,1,0),
    (1,5,7,2),
    (4,0,3,6),
)

colors = (
    (1,0,0),
    (0,1,0),
    (0,0,1),
    (0,0,0,),
    (1,1,1),
    (0,1,1),
    (1,0,0),
    (0,1,0),
    (0,0,1),
    (0,0,0,),
    (1,1,1),
    (0,1,1),    
)

def set_vertices(max_distance):
    #Create change between each cube
    x_value_change = random.randrange(-10, 10)
    y_value_change = random.randrange(-10, 10)
    z_value_change = random.randrange(-1 * max_distance, -20)

    new_vertices = []

    for vert in vertices: 
        new_vert = []

        new_x = vert[0] + x_value_change
        new_y = vert[1] + y_value_change
        new_z = vert[2] + z_value_change

        new_vert.append(new_x)
        new_vert.append(new_y)
        new_vert.append(new_z)

        new_vertices.append(new_vert) #Appends (1, 1, 1)
    return new_vertices

def Cube(veritces):
    glBegin(GL_QUADS)
    for surface in surfaces:
        x = 0
        for vertex in surface:
            x += 1
            glColor3fv((colors[x]))
            glVertex3fv(vertices[vertex])
    glEnd()

    glBegin(GL_LINES)
    for edge in edges:
        for vertex in edge:
            glVertex3fv(vertices[vertex]) #Draws vertex's in position given according to vertices array
    glEnd()


def main():
    pygame.init()
    display = (1000, 800)
    pygame.display.set_mode(display, DOUBLEBUF|OPENGL)

    gluPerspective(45, (display[0]/display[1]), 0.1, 50.0) #FOV, aspect ratio. clipping plane min, max

    glTranslatef(random.randrange(-5, 5), random.randrange(-5, 5), -40) #X,Y,Z -5 to zoom out on z axis
    #glRotatef(25, 1, 20, 0) #Degrees, x,y,z

    object_passed = False


    max_distance = 300
    cube_dict = {}

    for x in range(75): #Draws 75 cubes
        cube_dict[x] = set_vertices(max_distance) #Returns a new cube set of vertices

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    x_move = 0.3
                if event.key == pygame.K_RIGHT:
                    x_move = -0.3
                if event.key == pygame.K_UP:
                    y_move = -0.3
                if event.key == pygame.K_DOWN:
                    y_move = 0.3
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
                    x_move = 0
                if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
                    y_move = 0     
                    """
            if event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 4:
                    glTranslatef(0, 0, 1)
                if event.button == 5:
                    glTranslatef(0, 0, -1)
                    """

        #glRotatef(1, 1, 1, 1)

        x = glGetDoublev(GL_MODELVIEW_MATRIX)

        camera_x = x[3][0] #Access camera cordinates
        camera_y = x[3][1]
        camera_z = x[3][2]



        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) #Clears the screen

        glTranslatef(x_move, y_move, 0.5)

        for each_cube in cube_dict:
            Cube(cube_dict[each_cube])

        pygame.display.flip() #Cant use update
        pygame.time.wait(10)



main()

pygame.quit()
quit()

The tutorial is here

Thanks!!


回答1:


I have read some places that using glBegin and glEnd cause issues? What should I use instead ...

Drawing by glBegin and glEnd is deprecated in modern OpenGL (see Fixed Function Pipeline and Legacy OpenGL). In modern OpenGL Vertices are Specified by Vertex Buffer Objects and Vertex Array Object and evrything is drawn using a Shader program.

As a first step to this direction I recommend to use Vertex Buffer Objects and client-side capability

See 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 );
void SecondaryColorPointer( int size, enum type, sizei stride, const void *pointer );
void IndexPointer( enum type, sizei stride, const void *pointer );
void EdgeFlagPointer( sizei stride, const void *pointer );
void FogCoordPointer( enum type, sizei stride, const void *pointer );
void TexCoordPointer( int size, enum type, sizei stride, const void *pointer );

specify the location and organization of arrays to store vertex coordinates, normals, colors, secondary colors, color indices, edge flags, fog coordinates.

[...]

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, SECONDARY_COLOR_ARRAY, INDEX_ARRAY, EDGE_FLAG_ARRAY, FOG_COORD_ARRAY, or TEXTURE_COORD_ARRAY, for the vertex, normal, color, secondary color, color index, edge flag, fog coordinate, or texture coordinate array, respectively.

To do so you have to prepare and you have to include NumPy:

import numpy

Create global variables for the vertex buffer objects and create attribute sets (pairs of color and vertex coordinate) for the faces and create the vertex buffer objects for the faces (vertex coordinate and color). Finally create the vertex buffer object for the vertices coordinates of the edges:

def main():

    global face_vbos, edge_vbo

    .....

    # define the vertex buffers vor the faces

    vertex_array = []
    color_array = []
    for face in range(len(surfaces)):
        for vertex in surfaces[face]:
            vertex_array .append( vertices[vertex] )
            color_array.append( colors[face] )
    
    face_vbos = glGenBuffers(2)
    glBindBuffer(GL_ARRAY_BUFFER, face_vbos[0])
    glBufferData( GL_ARRAY_BUFFER, numpy.array( vertex_array, dtype=numpy.float32 ), GL_STATIC_DRAW )
    glBindBuffer(GL_ARRAY_BUFFER, face_vbos[1])
    glBufferData( GL_ARRAY_BUFFER, numpy.array( color_array, dtype=numpy.float32 ), GL_STATIC_DRAW )
    glBindBuffer(GL_ARRAY_BUFFER, 0)

    # define the vertex buffer for the edges
    edge_vbo = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, edge_vbo)
    glBufferData( GL_ARRAY_BUFFER, numpy.array( vertices, dtype=numpy.float32 ), GL_STATIC_DRAW )
    glBindBuffer(GL_ARRAY_BUFFER, 0)

    while True:

        # [...]

When you draw the faces and edges the you have define an array of vertex data (glVertexPointer ) and to define an array of colors (glColorPointer) to enable the client-side capability (glEnableClientState).
The faces can be drawn by glDrawArrays, since all the coordinates a colors are stored in an consecutive array (vertex_array and color_array -> face_vbos ).
The edges have to be drawn by glDrawElements, using the indices edges, since the vertices (vertices -> edge_vbo) have to be indexed to form lines:

def Cube(veritces):
    
    global face_vbos, edge_vbo 

    # draw faces
    
    glBindBuffer(GL_ARRAY_BUFFER, face_vbos[0])
    glVertexPointer( 3, GL_FLOAT, 0, None )
    glEnableClientState( GL_VERTEX_ARRAY )  
    glBindBuffer(GL_ARRAY_BUFFER, face_vbos[1]) 
    glColorPointer( 3, GL_FLOAT, 0, None ) 
    glEnableClientState( GL_COLOR_ARRAY ) 
    glBindBuffer(GL_ARRAY_BUFFER, 0) 

    glDrawArrays(GL_QUADS, 0, 6*4)

    glDisableClientState( GL_VERTEX_ARRAY )   
    glDisableClientState( GL_COLOR_ARRAY ) 
    
    #draw edges

    glBindBuffer(GL_ARRAY_BUFFER, edge_vbo)
    glVertexPointer( 3, GL_FLOAT, 0, None ) 
    glEnableClientState( GL_VERTEX_ARRAY ) 
    glBindBuffer(GL_ARRAY_BUFFER, 0) 

    glColor3f( 1, 1, 0 )
    glDrawElements(GL_LINES, 2*12, GL_UNSIGNED_INT, numpy.array( edges, dtype=numpy.uint32 ))

    glDisableClientState( GL_VERTEX_ARRAY )  

This can be further improved by using Vertex Array Objects and an Index buffer for the edges:

def main():

    global face_vao, edge_vao

    # [...]

    # define the vertex buffers vor the faces

    attribute_array = []
    for face in range(len(surfaces)):
        for vertex in surfaces[face ]:
            attribute_array.append( vertices[vertex] )
            attribute_array.append( colors[face] )
    
    face_vbos = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, face_vbos)
    glBufferData( GL_ARRAY_BUFFER, numpy.array( attribute_array, dtype=numpy.float32 ), GL_STATIC_DRAW )
    glBindBuffer(GL_ARRAY_BUFFER, 0)

    # define the vertex array object for the faces

    face_vao = glGenVertexArrays( 1 )
    glBindVertexArray( face_vao )

    glBindBuffer(GL_ARRAY_BUFFER, face_vbos)
    glVertexPointer( 3, GL_FLOAT, 6*4, None )
    glEnableClientState( GL_VERTEX_ARRAY )  
    glColorPointer( 3, GL_FLOAT, 6*4, ctypes.cast(3*4, ctypes.c_void_p) )
    glEnableClientState( GL_COLOR_ARRAY ) 
    glBindBuffer(GL_ARRAY_BUFFER, 0) 
    
    glBindVertexArray( 0 )

    # define the vertex buffer for the edges

    edge_vbo = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, edge_vbo)
    glBufferData( GL_ARRAY_BUFFER, numpy.array( vertices, dtype=numpy.float32 ), GL_STATIC_DRAW )
    glBindBuffer(GL_ARRAY_BUFFER, 0)

    # define the vertex array object for the edges

    edge_vao = glGenVertexArrays( 1 )
    glBindVertexArray( edge_vao )

    glBindBuffer(GL_ARRAY_BUFFER, edge_vbo)
    glVertexPointer( 3, GL_FLOAT, 0, None ) 
    glEnableClientState( GL_VERTEX_ARRAY ) 
    glBindBuffer(GL_ARRAY_BUFFER, 0) 

    edge_ibo = glGenBuffers(1)
    glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, edge_ibo )
    glBufferData( GL_ELEMENT_ARRAY_BUFFER, numpy.array( edges, dtype=numpy.uint32 ), GL_STATIC_DRAW )

    glBindVertexArray( 0 )
    glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 )

    while True:

        # [...]
def Cube(veritces):
    
    global face_vao, edge_vao

    # draw faces
    
    glBindVertexArray( face_vao )
    glDrawArrays( GL_QUADS, 0, 6*4 )
    glBindVertexArray( 0 )
    
    #draw edges

    glColor3f( 1, 1, 0 )
    glBindVertexArray( edge_vao )
    glDrawElements( GL_LINES, 2*12, GL_UNSIGNED_INT, None )
    glBindVertexArray( 0 )

An further performance imporvement you can gain by Face Culling and enbaling the Depth Test. The depth test should be less or eauel, so that the edges are not covered by the faces:

# enable depth test (less or equal)
glEnable( GL_DEPTH_TEST )
glDepthFunc( GL_LEQUAL )

# enable back face culling (front faces are drawn clockwise)
glEnable( GL_CULL_FACE )
glCullFace( GL_BACK )
glFrontFace( GL_CW )

Note, the last step to draw geometry in a "modern" way in OpenGL would be to use a Shader program and to replace glEnableClientState by glEnableVertexAttribArray and glVertexPointer respectively glColorPointer by glVertexAttribPointer (of course by using the proper parameters).

See also PyGame and OpenGL 4.



来源:https://stackoverflow.com/questions/50312760/pygame-opengl-3d-cube-lag

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!