使用 pyopengl 渲染 pygame 精灵

2024-01-07

在我的游戏中,我使用 pygame 来渲染精灵,但是该工作是在 CPU 端完成的,对于性能问题,如何使用 pyopengl 绘制这些精灵?那些天我开始学习c++中的opengl,它和python类似吗?


如果你只想在 GPU 上渲染精灵,你可以使用ModernGL https://moderngl.readthedocs.io/en/latest/。 ModenGL 在底层使用 OpenGL。

写一个类,它的子类pygame.sprite.Group https://www.pygame.org/docs/ref/sprite.html#pygame.sprite.Group并覆盖draw方法。使用moderngl.VertexArray https://moderngl.readthedocs.io/en/latest/reference/vertex_array.html and moderngl.Texture https://moderngl.readthedocs.io/en/latest/reference/texture.html渲染精灵。类似的事情可以使用原生 OpenGL (PyOpenGL) 来完成,只是需要多一点代码。
也可以看看PyGame 和 OpenGL https://github.com/Rabbid76/PyGameExamplesAndAnswers/blob/master/documentation/pygame_opengl/pygame_opengl__site_map.md

最小的例子:

import pygame
import moderngl
import ctypes

vertex_shader_sprite = """
#version 330
in vec2 in_position;
in vec2 in_uv;
out vec2 v_uv;
void main()
{
    v_uv = in_uv;
    gl_Position = vec4(in_position, 0.0, 1.0);
}
"""

fragment_shader_sprite = """
#version 330
out vec4 fragColor;
uniform sampler2D u_texture;
in vec2 v_uv;
void main() 
{
    fragColor = texture(u_texture, v_uv);
}
"""

class ModernGLGroup(pygame.sprite.Group):

    gl_context = None
    gl_program = None
    gl_buffer = None
    gl_vao = None
    gl_textures = {}

    def __init__(self, sprites = None):
        if sprites == None:
            super().__init__() 
        else:
            super().__init__(sprites) 

    def get_program():
        if ModernGLGroup.gl_program == None:
            ModernGLGroup.gl_program = ModernGLGroup.gl_context.program(
                vertex_shader = vertex_shader_sprite,
                fragment_shader = fragment_shader_sprite)
        return ModernGLGroup.gl_program
    
    def get_buffer():
        if ModernGLGroup.gl_buffer == None:
            ModernGLGroup.gl_buffer = ModernGLGroup.gl_context.buffer(None, reserve=6*4*4)
        return ModernGLGroup.gl_buffer

    def get_vao():
        if ModernGLGroup.gl_vao == None:
            ModernGLGroup.gl_vao = ModernGLGroup.gl_context.vertex_array(
                ModernGLGroup.get_program(), [(ModernGLGroup.get_buffer(), "2f4 2f4", "in_position", "in_uv")])
        return ModernGLGroup.gl_vao

    def get_texture(image):
        if not image in ModernGLGroup.gl_textures:
            rgba_image = image.convert_alpha()
            texture = ModernGLGroup.gl_context.texture(rgba_image.get_size(), 4, rgba_image.get_buffer())
            texture.swizzle = 'BGRA'
            ModernGLGroup.gl_textures[image] = texture
        return ModernGLGroup.gl_textures[image]

    def convert_vertex(pt, surface):
        return pt[0] / surface.get_width() * 2 - 1, 1 - pt[1] / surface.get_height() * 2 

    def render(sprite, surface):
        corners = [
            ModernGLGroup.convert_vertex(sprite.rect.bottomleft, surface),
            ModernGLGroup.convert_vertex(sprite.rect.bottomright, surface),
            ModernGLGroup.convert_vertex(sprite.rect.topright, surface),
            ModernGLGroup.convert_vertex(sprite.rect.topleft, surface)] 
        vertices_quad_2d = (ctypes.c_float * (6*4))(
            *corners[0], 0.0, 1.0, 
            *corners[1], 1.0, 1.0, 
            *corners[2], 1.0, 0.0,
            *corners[0], 0.0, 1.0, 
            *corners[2], 1.0, 0.0, 
            *corners[3], 0.0, 0.0)
        
        ModernGLGroup.get_buffer().write(vertices_quad_2d)
        ModernGLGroup.get_texture(sprite.image).use(0)
        ModernGLGroup.get_vao().render()    

    def draw(self, surface):
        for sprite in self:
            ModernGLGroup.render(sprite, surface)

class SpriteObject(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__() 
        try:
            self.image = pygame.image.load('AirPlaneFront1-256.png').convert_alpha()
        except:
            self.image = pygame.Surface((100, 100), pygame.SRCALPHA)
            pygame.draw.circle(self.image, (255, 255, 0), (50, 50), 50)
        self.rect = self.image.get_rect(center = (x, y))
       
    def update(self, surface):
        keys = pygame.key.get_pressed()
        vel = 5
        if keys[pygame.K_LEFT]:
            self.rect.left = max(0, self.rect.left-vel)
        if keys[pygame.K_RIGHT]:
            self.rect.right = min(surface.get_width(), self.rect.right+vel)
        if keys[pygame.K_UP]:
            self.rect.top = max(0, self.rect.top-vel)
        if keys[pygame.K_DOWN]:
            self.rect.bottom = min(surface.get_height(), self.rect.bottom+vel)

pygame.init()
window = pygame.display.set_mode((500, 500), pygame.DOUBLEBUF | pygame.OPENGL)
clock = pygame.time.Clock()

gl_context = moderngl.create_context()
gl_context.enable(moderngl.BLEND)
ModernGLGroup.gl_context = gl_context

sprite_object = SpriteObject(*window.get_rect().center)
group = ModernGLGroup(sprite_object)

run = True
while run:
    clock.tick(60)
    event_list = pygame.event.get()
    for event in event_list:
        if event.type == pygame.QUIT:
            run = False

    group.update(window)

    gl_context.clear(0.2, 0.2, 0.2)
    group.draw(window)
    pygame.display.flip()

pygame.quit()
exit()
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 pyopengl 渲染 pygame 精灵 的相关文章

随机推荐