为什么我的pyglet程序在渲染128粒子时变慢?

时间:2017-12-25 11:22:59

标签: python python-3.x performance opengl pyglet

为什么我的程序在渲染128个粒子时会变慢?我认为这还不足以达不到30 fps。

我所做的只是渲染128个粒子并给它们一些基本的引力

on_draw函数

 def on_draw(self, time=None):
    glClearColor(0.0, 0.0, 0.0, 1.0)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()

    self.particles.append(Particle())
    for particle in self.particles:
        particle.draw()

        if particle.is_dead:
            self.particles.remove(particle)

粒子类

class Particle:

    def __init__(self, **kwargs):
        self.acceleration = Vector2(0, 0.05)
        self.velocity = Vector2(random.uniform(-1, 1), random.uniform(-1, 0))
        self.position = Vector2()
        self.time_to_live = 255

        self.numpoints = 50

        self._vertices = []

        for i in range(self.numpoints):
            angle = math.radians(float(i) / self.numpoints * 360.0)
            x = 10 * math.cos(angle) + self.velocity[0] + 300
            y = 10 * math.sin(angle) + self.velocity[1] + 400
            self._vertices += [x, y]

    def update(self, time=None):
        self.velocity += self.acceleration
        self.position -= self.velocity
        self.time_to_live -= 2

    def draw(self):
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        glPushMatrix()
        glTranslatef(self.position[0], self.position[1], 0)
        pyglet.graphics.draw(self.numpoints, GL_TRIANGLE_FAN, ('v2f', self._vertices), ('c4B', self.color))
        glPopMatrix()

        self.update()

    @property
    def is_dead(self):
        if self.time_to_live <= 0:
            return True
        return False

    @property
    def color(self):
        return tuple(color for i in range(self.numpoints) for color in (255, 255, 255,  self.time_to_live))

1 个答案:

答案 0 :(得分:3)

我对使用GL_TRIANGLE_FAN并不是很满意,因为它在使用批量渲染时会导致很多奇怪的形状。因此,请考虑转移到GL_TRIANGLES并简单地将所有点添加到对象,而不是依靠GL来为您关闭形状。

这样,您可以轻松转移到批量渲染

import pyglet
from pyglet.gl import *
from collections import OrderedDict
from time import time, sleep
from math import *
from random import randint

key = pyglet.window.key

class CustomGroup(pyglet.graphics.Group):
    def set_state(self):
        #pyglet.gl.glLineWidth(5)
        #glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
        #glColor4f(1, 0, 0, 1) #FFFFFF
        #glLineWidth(1)
        #glEnable(texture.target)
        #glBindTexture(texture.target, texture.id)
        pass

    def unset_state(self):
        glLineWidth(1)
        #glDisable(texture.target)

class Particle():
    def __init__(self, x, y, batch, particles):
        self.batch = batch
        self.particles = particles
        self.group = CustomGroup()

        self.add_point(x, y)

    def add_point(self, x, y):

        colors = ()#255,0,0

        sides = 50
        radius = 25

        deg = 360/sides
        points = ()#x, y # Starting point is x, y?

        prev = None
        for i in range(sides):

            n = ((deg*i)/180)*pi # Convert degrees to radians
            point = int(radius * cos(n)) + x, int(radius * sin(n)) + y

            if prev:
                points += x, y
                points += prev
                points += point
                colors += (255, i*int(255/sides), 0)*3 # Add a color pair for each point (r,g,b) * points[3 points added]

            prev = point

        points += x, y
        points += prev
        points += points[2:4]
        colors += (255, 0, 255)*3

        self.particles[len(self.particles)] = self.batch.add(int(len(points)/2), pyglet.gl.GL_TRIANGLES, self.group, ('v2i/stream', points), ('c3B', colors))

class main(pyglet.window.Window):
    def __init__ (self, demo=False):
        super(main, self).__init__(800, 600, fullscreen = False, vsync = True)
        #print(self.context.config.sample_buffers)
        self.x, self.y = 0, 0

        self.sprites = OrderedDict()
        self.batches = OrderedDict()
        self.batches['default'] = pyglet.graphics.Batch()
        self.active_batch = 'default'

        for i in range(1000):
            self.sprites[len(self.sprites)] = Particle(randint(0, 800), randint(0, 600), self.batches[self.active_batch], self.sprites)

        self.alive = True

        self.fps = 0
        self.last_fps = time()
        self.fps_label = pyglet.text.Label(str(self.fps) + ' fps', font_size=12, x=3, y=self.height-15)

    def on_draw(self):
        self.render()

    def on_close(self):
        self.alive = 0

    def on_key_press(self, symbol, modifiers):
        if symbol == key.ESCAPE: # [ESC]
            self.alive = 0

    def render(self):
        self.clear()
        #self.bg.draw()

        self.batches[self.active_batch].draw()

        self.fps += 1
        if time()-self.last_fps > 1:
            self.fps_label.text = str(self.fps) + ' fps'
            self.fps = 0
            self.last_fps = time()

        self.fps_label.draw()
        self.flip()

    def run(self):
        while self.alive == 1:
            self.render()

            # -----------> This is key <----------
            # This is what replaces pyglet.app.run()
            # but is required for the GUI to not freeze
            #
            event = self.dispatch_events()

if __name__ == '__main__':
    x = main(demo=True)
    x.run()

请记住,在我的nVidia 1070上,我设法从这段代码中获得了大约35fps,这并不令人头疼。但它是 1000个对象 *边,给予或接受。

我改变的主要是:

self.batch.add(int(len(points)/2), pyglet.gl.GL_TRIANGLES, self.group, ('v2i/stream', points), ('c3B', colors))

在你的绘制循环中,你会做:

self.batch.draw()

而不是为每个粒子对象调用Particle.draw() 它的作用是它将所有对象以一个巨大的批量发送到图形卡,而不是告诉图形卡逐个呈现对象。

正如@thokra指出的那样,你的代码比GPU密集型更加占用CPU 希望这可以解决它或给你一些指示。

这段代码的大部分内容来自我与我的好朋友一起做过的局域网项目:

因为我没有你所有的代码,主要是主循环。我将你的问题应用到我自己的代码中并通过调整它来“解决”它。再次,如果您需要,希望它能帮助并从该github项目中窃取想法。新年快乐!