2016-03-03 37 views
0

我正在使用基于SDL 1 C++库的Python库“Pygame”升级到使用更现代的OpenGL代码。我有一个绘制矩形2000一个简单的Pygame程序:如何加速使用Python,Pyglet和顶点缓冲区编写的OpenGL代码?

import pygame 
import random 
import time 

# Define some colors 
BLACK = (0, 0, 0) 
WHITE = (255, 255, 255) 
GREEN = (0, 255, 0) 
RED = (255, 0, 0) 


class Rectangle(): 
    '''Draw a rectangle''' 
    def __init__(self): 
     '''These are the rectangle's attributes.''' 
     self.x = 0 
     self.y = 0 
     self.width = 0 
     self.height = 0 
     self.change_x = 0 
     self.change_y = 0 
     self.color = [0, 0, 0] 

    def draw(self, screen): 
     '''Drawing the rectangle.''' 
     pygame.draw.rect(screen, self.color, [self.x, self.y, self.width, self.height]) 

    def move(self): 
     '''Moving the rectangle around the screen.''' 
     self.x += self.change_x 
     self.y += self.change_y 


def main(): 
    pygame.init() 

    # Set the width and height of the screen [width, height] 
    size = (700, 500) 
    screen = pygame.display.set_mode(size) 

    pygame.display.set_caption("My Game") 

    # Loop until the user clicks the close button. 
    done = False 

    # Used to manage how fast the screen updates 
    clock = pygame.time.Clock() 

    my_list = [] 
    color_list = [] 
    '''Creates 1000 rectangles.''' 
    for i in range(2000): 
     my_object = Rectangle() 

     r = random.randrange(256) 
     g = random.randrange(256) 
     b = random.randrange(256) 
     my_object.color = [r, g, b] 
     color_list.append(my_object) 

     my_object.x = random.randrange(701) 
     my_object.y = random.randrange(501) 

     my_object.change_x = random.randrange(-3, 4) 
     my_object.change_y = random.randrange(-3, 4) 

     my_object.width = random.randrange(20, 71) 
     my_object.height = random.randrange(20, 71) 

     my_list.append(my_object) 

    # -------- Main Program Loop ----------- 
    while not done: 
     # --- Main event loop 
     for event in pygame.event.get(): 
      if event.type == pygame.QUIT: 
       done = True 

     screen.fill(WHITE) 

     # Start the clock 
     start = time.time() 

     for item in my_list: 
      item.draw(screen) 
      item.move() 

     # --- Go ahead and update the screen with what we've drawn. 
     pygame.display.flip() 

     # End the clock 
     elapsed = time.time() - start 
     print(elapsed) 

     # --- Limit to 60 frames per second 
     clock.tick(60) 

    # Close the window and quit. 
    pygame.quit() 

main() 

在我的电脑需要大约0.012秒,得出每一帧。

我也有一个程序使用Python Pyglet库来访问OpenGL。我正在使用顶点缓冲区来加快速度。这里的代码:

""" 
This example uses OpenGL via Pyglet and draws 
a bunch of rectangles on the screen. 
""" 

import random 
import time 
import pyglet.gl as GL 
import pyglet 
import ctypes 

# Set up the constants 
SCREEN_WIDTH = 700 
SCREEN_HEIGHT = 500 

RECT_WIDTH = 50 
RECT_HEIGHT = 50 


class VertexBuffer(): 
    """ Class to hold vertex buffer info. """ 
    def __init__(self, vbo_id, size, width, height, color): 
     self.vbo_id = vbo_id 
     self.size = size 
     self.width = width 
     self.height = height 
     self.color = color 


def create_rect(width, height, color): 
    """ Create a vertex buffer for a rectangle. """ 
    v2f = [-width/2, -height/2, 
      width/2, -height/2, 
      width/2, height/2, 
      -width/2, height/2] 

    vbo_id = GL.GLuint() 

    GL.glGenBuffers(1, ctypes.pointer(vbo_id)) 

    data2 = (GL.GLfloat*len(v2f))(*v2f) 

    GL.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo_id) 
    GL.glBufferData(GL.GL_ARRAY_BUFFER, ctypes.sizeof(data2), data2, GL.GL_STATIC_DRAW) 

    shape = VertexBuffer(vbo_id, len(v2f)//2, width, height, color) 
    return shape 


def render_rect_filled(shape, x, y): 
    """ Render the shape at the right spot. """ 
    # Set color 
    GL.glDisable(GL.GL_BLEND) 
    GL.glColor4ub(shape.color[0], shape.color[1], shape.color[2], 255) 

    GL.glBindBuffer(GL.GL_ARRAY_BUFFER, shape.vbo_id) 
    GL.glVertexPointer(2, GL.GL_FLOAT, 0, 0) 

    GL.glLoadIdentity() 
    GL.glTranslatef(x + shape.width/2, y + shape.height/2, 0) 

    GL.glDrawArrays(GL.GL_QUADS, 0, shape.size) 


class Rectangle(): 

    def __init__(self, x, y, width, height, delta_x, delta_y, color): 
     self.x = x 
     self.y = y 
     self.width = width 
     self.height = height 
     self.delta_x = delta_x 
     self.delta_y = delta_y 
     self.color = color 
     self.vbo = create_rect(self.width, self.height, self.color) 

    def move(self): 
     self.x += self.delta_x 
     self.y += self.delta_y 

    def draw(self): 
     render_rect_filled(self.vbo, self.x, self.y) 


class MyApplication(): 
    """ Main application class. """ 

    def setup(self): 
     """ Set up the game and initialize the variables. """ 

     # Set background to white 
     GL.glClearColor(1, 1, 1, 1) 

     self.shape_list = [] 

     for i in range(2000): 
      x = random.randrange(0, SCREEN_WIDTH) 
      y = random.randrange(0, SCREEN_HEIGHT) 
      width = random.randrange(20, 71) 
      height = random.randrange(20, 71) 

      d_x = random.randrange(-3, 4) 
      d_y = random.randrange(-3, 4) 

      red = random.randrange(256) 
      green = random.randrange(256) 
      blue = random.randrange(256) 
      alpha = random.randrange(256) 
      shape_type = random.randrange(2) 
      shape = Rectangle(x, y, width, height, d_x, d_y, (red, green, blue)) 
      self.shape_list.append(shape) 

    def animate(self, dt): 
     """ Move everything """ 

     for shape in self.shape_list: 
      shape.move() 

    def on_draw(self): 
     """ 
     Render the screen. 
     """ 
     start = time.time() 

     GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) 
     GL.glMatrixMode(GL.GL_MODELVIEW) 
     GL.glEnableClientState(GL.GL_VERTEX_ARRAY) 

     for shape in self.shape_list: 
      shape.draw() 

     elapsed = time.time() - start 
     print(elapsed) 


def main(): 
    window = pyglet.window.Window(SCREEN_WIDTH, SCREEN_HEIGHT) 
    app = MyApplication() 
    app.setup() 
    pyglet.clock.schedule_interval(app.animate, 1/60) 

    @window.event 
    def on_draw(): 
     window.clear() 
     app.on_draw() 

    pyglet.app.run() 

main() 

OpenGL代码需要0.056秒来绘制每个帧。我希望能够接近Pygame的表现。甚至更好。我可以采取哪些OpenGL技巧来提高速度?

回答

0

好的,根据我从Reddit学到的知识,我可以通过两件事来加速这个过程。

与其使用2000个VBO,请使用其中一个。这里是一个更快的例子:

""" 
This example uses OpenGL via Pyglet and draws 
a bunch of rectangles on the screen. 
""" 

import random 
import time 
import pyglet.gl as GL 
import pyglet 
import ctypes 

# Set up the constants 
SCREEN_WIDTH = 700 
SCREEN_HEIGHT = 500 

RECT_WIDTH = 50 
RECT_HEIGHT = 50 


def render_rect_filled(shape, offset): 
    """ Render the shape at the right spot. """ 
    # Set color 
    GL.glLoadIdentity() 
    GL.glColor3ub(shape.color[0], shape.color[1], shape.color[2]) 

    GL.glTranslatef(shape.x + shape.width/2, shape.y + shape.height/2, 0) 

    GL.glDrawArrays(GL.GL_QUADS, offset, 4) 


class Rectangle(): 

    def __init__(self, x, y, width, height, delta_x, delta_y, color): 
     self.x = x 
     self.y = y 
     self.width = width 
     self.height = height 
     self.delta_x = delta_x 
     self.delta_y = delta_y 
     self.color = color 

    def move(self): 
     self.x += self.delta_x 
     self.y += self.delta_y 

def create_rects(rect_list): 
    """ Create a vertex buffer for a set of rectangles. """ 

    v2f = [] 
    for shape in rect_list: 
     v2f.extend ([-shape.width/2, -shape.height/2, 
       shape.width/2, -shape.height/2, 
       shape.width/2, shape.height/2, 
       -shape.width/2, shape.height/2]) 

    vbo_id = GL.GLuint() 

    GL.glGenBuffers(1, ctypes.pointer(vbo_id)) 

    data2 = (GL.GLfloat*len(v2f))(*v2f) 

    GL.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo_id) 
    GL.glBufferData(GL.GL_ARRAY_BUFFER, ctypes.sizeof(data2), data2, GL.GL_STATIC_DRAW) 

    return vbo_id 


class MyApplication(): 
    """ Main application class. """ 

    def setup(self): 
     """ Set up the game and initialize the variables. """ 

     # Set background to white 
     GL.glClearColor(1, 1, 1, 1) 

     self.shape_list = [] 

     for i in range(2000): 
      x = random.randrange(0, SCREEN_WIDTH) 
      y = random.randrange(0, SCREEN_HEIGHT) 
      width = random.randrange(20, 71) 
      height = random.randrange(20, 71) 

      d_x = random.randrange(-3, 4) 
      d_y = random.randrange(-3, 4) 

      red = random.randrange(256) 
      green = random.randrange(256) 
      blue = random.randrange(256) 
      alpha = random.randrange(256) 
      shape_type = random.randrange(2) 
      shape = Rectangle(x, y, width, height, d_x, d_y, (red, green, blue)) 
      self.shape_list.append(shape) 

     self.vertex_vbo_id = create_rects(self.shape_list) 

    def animate(self, dt): 
     """ Move everything """ 

     for shape in self.shape_list: 
      shape.move() 

    def on_draw(self): 
     """ 
     Render the screen. 
     """ 
     start = time.time() 

     GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) 
     GL.glMatrixMode(GL.GL_MODELVIEW) 
     GL.glEnableClientState(GL.GL_VERTEX_ARRAY) 

     GL.glDisable(GL.GL_BLEND) 
     GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vertex_vbo_id) 
     GL.glVertexPointer(2, GL.GL_FLOAT, 0, 0) 


     offset = 0 
     for shape in self.shape_list: 
      render_rect_filled(shape, offset) 
      offset += 4 

     elapsed = time.time() - start 
     print(elapsed) 


def main(): 
    window = pyglet.window.Window(SCREEN_WIDTH, SCREEN_HEIGHT) 
    app = MyApplication() 
    app.setup() 
    pyglet.clock.schedule_interval(app.animate, 1/60) 

    @window.event 
    def on_draw(): 
     window.clear() 
     app.on_draw() 

    pyglet.app.run() 

main() 

此外,您可以使用颜色的VBOs。这使用VBOs,甚至更快:

""" 
This example uses OpenGL via Pyglet and draws 
a bunch of rectangles on the screen. 
""" 

import random 
import time 
import pyglet.gl as GL 
import pyglet 
import ctypes 

# Set up the constants 
SCREEN_WIDTH = 700 
SCREEN_HEIGHT = 500 

RECT_WIDTH = 50 
RECT_HEIGHT = 50 


def create_rect(width, height, color): 
    """ Create a vertex buffer for a rectangle. """ 
    v2f = [-width/2, -height/2, 
      width/2, -height/2, 
      width/2, height/2, 
      -width/2, height/2] 

    vbo_id = GL.GLuint() 

    GL.glGenBuffers(1, ctypes.pointer(vbo_id)) 

    data2 = (GL.GLfloat*len(v2f))(*v2f) 

    GL.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo_id) 
    GL.glBufferData(GL.GL_ARRAY_BUFFER, ctypes.sizeof(data2), data2, GL.GL_STATIC_DRAW) 

    shape = VertexBuffer(vbo_id, len(v2f)//2, width, height, color) 
    return shape 


def render_rect_filled(shape, offset): 
    """ Render the shape at the right spot. """ 
    # Set color 
    GL.glLoadIdentity() 
    #GL.glColor4ub(shape.color[0], shape.color[1], shape.color[2], 255) 

    GL.glTranslatef(shape.x + shape.width/2, shape.y + shape.height/2, 0) 

    GL.glDrawArrays(GL.GL_QUADS, offset, 4) 


class Rectangle(): 

    def __init__(self, x, y, width, height, delta_x, delta_y, color): 
     self.x = x 
     self.y = y 
     self.width = width 
     self.height = height 
     self.delta_x = delta_x 
     self.delta_y = delta_y 
     self.color = color 

    def move(self): 
     self.x += self.delta_x 
     self.y += self.delta_y 

def create_rects(rect_list): 
    """ Create a vertex buffer for a set of rectangles. """ 

    v2f = [] 
    for shape in rect_list: 
     v2f.extend ([-shape.width/2, -shape.height/2, 
       shape.width/2, -shape.height/2, 
       shape.width/2, shape.height/2, 
       -shape.width/2, shape.height/2]) 

    vbo_id = GL.GLuint() 

    GL.glGenBuffers(1, ctypes.pointer(vbo_id)) 

    data2 = (GL.GLfloat*len(v2f))(*v2f) 

    GL.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo_id) 
    GL.glBufferData(GL.GL_ARRAY_BUFFER, ctypes.sizeof(data2), data2, GL.GL_STATIC_DRAW) 

    return vbo_id 

def create_colors(rect_list): 
    """ Create a vertex buffer for a set of rectangles. """ 

    v2f = [] 
    for shape in rect_list: 
     for i in range(4): 
      v2f.extend(shape.color) 

    vbo_id = GL.GLuint() 

    GL.glGenBuffers(1, ctypes.pointer(vbo_id)) 

    data2 = (GL.GLfloat*len(v2f))(*v2f) 

    GL.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo_id) 
    GL.glBufferData(GL.GL_ARRAY_BUFFER, ctypes.sizeof(data2), data2, GL.GL_STATIC_DRAW) 

    return vbo_id 

class MyApplication(): 
    """ Main application class. """ 

    def setup(self): 
     """ Set up the game and initialize the variables. """ 

     # Set background to white 
     GL.glClearColor(1, 1, 1, 1) 

     self.shape_list = [] 

     for i in range(2000): 
      x = random.randrange(0, SCREEN_WIDTH) 
      y = random.randrange(0, SCREEN_HEIGHT) 
      width = random.randrange(20, 71) 
      height = random.randrange(20, 71) 

      d_x = random.randrange(-3, 4) 
      d_y = random.randrange(-3, 4) 

      red = random.random() 
      green = random.random() 
      blue = random.random() 
      shape = Rectangle(x, y, width, height, d_x, d_y, (red, green, blue)) 
      self.shape_list.append(shape) 

     self.vertex_vbo_id = create_rects(self.shape_list) 
     self.color_vbo_id = create_colors(self.shape_list) 

    def animate(self, dt): 
     """ Move everything """ 

     for shape in self.shape_list: 
      shape.move() 

    def on_draw(self): 
     """ 
     Render the screen. 
     """ 
     start = time.time() 

     GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) 
     GL.glMatrixMode(GL.GL_MODELVIEW) 
     GL.glDisable(GL.GL_BLEND) 

     GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vertex_vbo_id) 
     GL.glEnableClientState(GL.GL_VERTEX_ARRAY) 
     GL.glVertexPointer(2, GL.GL_FLOAT, 0, 0) 

     GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.color_vbo_id) 
     GL.glEnableClientState(GL.GL_COLOR_ARRAY) 
     GL.glColorPointer(3, GL.GL_FLOAT, 0, 0) 

     offset = 0 
     for shape in self.shape_list: 
      render_rect_filled(shape, offset) 
      offset += 4 

     elapsed = time.time() - start 
     print(elapsed) 


def main(): 
    window = pyglet.window.Window(SCREEN_WIDTH, SCREEN_HEIGHT) 
    app = MyApplication() 
    app.setup() 
    pyglet.clock.schedule_interval(app.animate, 1/60) 

    @window.event 
    def on_draw(): 
     window.clear() 
     app.on_draw() 

    pyglet.app.run() 

main()