Рисовать объекты в современном контексте в Pygame - PullRequest
0 голосов
/ 21 июня 2020

Я написал эту простую программу, которая отображает изображение в контексте moderngl, используя pygame, и моя цель - нарисовать прямоугольник на экране. (Это, очевидно, всего лишь минимальный рабочий пример , и настоящая цель состоит в том, чтобы оживить текстуру и создать на экране больше прямоугольника).

import moderngl
import numpy as np
import pygame
from PIL import Image


class MyPyGame:
    def __init__(self):
        pygame.init()
        self._surface = pygame.display.set_mode((800, 600), pygame.DOUBLEBUF | pygame.OPENGL)

        self.context = moderngl.create_context()

        image = Image.open("test.jpg").transpose(Image.FLIP_TOP_BOTTOM)
        self.texture = self.context.texture(image.size, 3, image.tobytes())
        self.texture.use(0)

        self.program = self.context.program(
            vertex_shader="""
            #version 330
            in vec2 in_position;
            out vec2 uv0;
            void main() {
                gl_Position = vec4(in_position, 0.0, 1.0);
                uv0 = (0.5 * in_position) + vec2(0.5);
            }
            """,
            fragment_shader="""
            #version 330
            out vec4 fragColor;
            uniform sampler2D texture0;
            in vec2 uv0;
            void main() {
                fragColor = texture(texture0, uv0);
            }
            """)

        vertices_quad_2d = np.array([-1.0, 1.0, -1.0, -1.0, 1.0, -1.0,
                                     -1.0, 1.0, 1.0, -1.0, 1.0, 1.0], dtype=np.float32)
        vertex_buffer_quad_2d = self.context.buffer(vertices_quad_2d.tobytes())
        self.vertex_array = self.context.vertex_array(self.program, [(vertex_buffer_quad_2d, "2f", "in_position")])

    def run(self):
        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    quit()
            self.vertex_array.render()
            pygame.display.flip()

if __name__ == '__main__':
    app = MyPyGame()
    app.run()

Некоторые вещи, которые я пробовал:

  1. Нарисуйте прямоугольник обычным способом с помощью pygame, то есть добавьте эту строку в основной метод run либо до, либо после отрисовки массива вершин ( оба не работают):

     pygame.draw.rect(self._surface, (200, 200, 200), [100, 100, 100, 100])
    
  2. Вместо флага pygame.OPENGL используйте флаг pygame.OPENGLBLIT, а затем blit прямоугольник (не сработало, подробнее здесь )

  3. Используйте контекст moderngl как окно внутри основного дисплея, как предлагается здесь , а затем blit прямоугольник на главный дисплей. Это , а не то, к чему я стремлюсь, я хочу отображать текст и формы в контексте, но все же хотел хотя бы попробовать это. Также не сработал, приведенный ниже код (заменяющий соответствующий код в __init__) привел к исключению cannot detect OpenGL context:

     pygame.init()
     self.main_window = pygame.display.set_mode((800, 600))
     self.graphics_window = pygame.Surface((700, 600), pygame.DOUBLEBUF | pygame.OPENGL)
     self.main_window.blit(self.graphics_window, (100, 0))
     self.context = moderngl.create_context()
    
  4. Используйте "из коробки" окно из moderngl_window с использованием pygame ( здесь код ). Опять же, не удалось нарисовать прямоугольник на самом контексте - попытался добавить ту же строку из (1) либо внутри самого кода окна, либо когда я пишу свое собственное окно, которое наследуется от этого окна pygame.

[Работаем над: Windows10, python3 .6, pygame 1.9.6, moderngl 5.6.1]

Как создать окно, одновременно отображающее графику и есть слой предметов костюма? (текст, кнопки и т. д. c.)

РЕДАКТИРОВАТЬ: Возможно, необходимо пояснение для моей мотивации: я хотел бы иметь один слой графики в фоновом режиме, что в приведенном выше примере представляет собой некоторую манипуляцию с входное изображение. Затем, на переднем плане, я хотел бы иметь некоторые объекты PyGame костюма, такие как геометрические c формы, кнопки и т. Д. c.

Ответы [ 2 ]

1 голос
/ 29 июля 2020

При смешивании moderngl и pygame вы, вероятно, захотите, чтобы moderngl выполнял все отрисовки на экране. Вещи, которые вы визуализируете с помощью pygame, должны отображаться на отдельной поверхности. Затем эта поверхность преобразуется в moderngl.Texture, чтобы мы могли нарисовать ее на экране с помощью OpenGL.

В проекте moderngl-window есть даже официальный пример: https://github.com/moderngl/moderngl-window/blob/master/examples/advanced/pygame2.py

Если у вас есть вопросы об инициализации окна pygame, вы можете посмотреть бэкэнд окна pygame: https://github.com/moderngl/moderngl-window/blob/5cf117ca9c0ef8cbde772d51041a039fc611b6c7/moderngl_window/context/pygame2/window.py#L28 -L65

Я создал новый пример, который отображает поверхность pygame к экрану. Это альфа-наложение поверх текущего фона. Все, что вы визуализируете в pygame, необходимо визуализировать на этой поверхности и загружать в графическую память каждый кадр.

Новый пример находится здесь: https://github.com/moderngl/moderngl-window/blob/master/examples/advanced/pygame2_simple.py

import math
from pathlib import Path
import pygame
import moderngl
import moderngl_window
from moderngl_window import geometry


class Pygame(moderngl_window.WindowConfig):
    """
    Example using pygame with moderngl.
    Needs to run with ``--window pygame2`` option.
    """
    title = "Pygame"
    window_size = 1280, 720
    resource_dir = (Path(__file__) / '../../resources').absolute()

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        if self.wnd.name != 'pygame2':
            raise RuntimeError('This example only works with --window pygame2 option')

        self.pg_res = (320, 180)
        # Create a 24bit (rgba) offscreen surface pygame can render to
        self.pg_screen = pygame.Surface(self.pg_res, flags=pygame.SRCALPHA)
        # 24 bit (rgba) moderngl texture
        self.pg_texture = self.ctx.texture(self.pg_res, 4)
        self.pg_texture.filter = moderngl.NEAREST, moderngl.NEAREST

        self.texture_program = self.load_program('programs/texture.glsl')
        self.quad_fs = geometry.quad_fs()

    def render(self, time, frametime):
        self.render_pygame(time)

        self.ctx.clear(
            (math.sin(time) + 1.0) / 2,
            (math.sin(time + 2) + 1.0) / 2,
            (math.sin(time + 3) + 1.0) / 2,
        )
    
        self.ctx.enable(moderngl.BLEND)
        self.pg_texture.use()
        self.quad_fs.render(self.texture_program)
        self.ctx.disable(moderngl.BLEND)

    def render_pygame(self, time):
        """Render to offscreen surface and copy result into moderngl texture"""
        self.pg_screen.fill((0, 0, 0, 0))  # Make sure we clear with alpha 0!
        N = 8
        for i in range(N):
            time_offset = 6.28 / N * i
            pygame.draw.circle(
                self.pg_screen,
                ((i * 50) % 255, (i * 100) % 255, (i * 20) % 255),
                (
                    math.sin(time + time_offset) * 55 + self.pg_res[0] // 2,
                    math.cos(time + time_offset) * 55 + self.pg_res[1] // 2),
                math.sin(time) * 4 + 15,
            )

        # Get the buffer view of the Surface's pixels
        # and write this data into the texture
        texture_data = self.pg_screen.get_view('1')
        self.pg_texture.write(texture_data)


if __name__ == '__main__':
    Pygame.run()
0 голосов
/ 15 августа 2020

В значительной степени основан на ответе @ Grimmy, приведенном выше, вот код, который объединяет modernl-window и PyGame2, он основан на некоторых примерах из репозитория moderngl-window:

from pathlib import Path
import pygame
import moderngl
import moderngl_window
from moderngl_window import geometry


class Pygame(moderngl_window.WindowConfig):
    title = "Pygame"
    window_size = 1280, 720
    resource_dir = (Path(__file__) / '../../resources').absolute()

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        # Create a 24bit (rgba) offscreen surface pygame can render to
        self.pg_screen = pygame.Surface(self.window_size, flags=pygame.SRCALPHA)
        # 24 bit (rgba) moderngl texture
        self.pg_texture = self.ctx.texture(self.window_size, 4)
        self.pg_texture.filter = moderngl.NEAREST, moderngl.NEAREST

        self.texture_program = self.load_program('programs/texture.glsl')
        self.quad_texture = self.load_texture_2d('textures/python-bg.png')
        self.quad_fs = geometry.quad_fs()

    def render(self, time, frametime):
        self.ctx.clear()

        self.ctx.enable(moderngl.BLEND)

        # Render background graphics
        self.quad_texture.use()
        self.texture_program['texture0'].value = 0
        self.quad_fs.render(self.texture_program)

        # Render foreground objects
        self.pg_texture.use()
        self.render_pygame(time)
        self.quad_fs.render(self.texture_program)

        self.ctx.disable(moderngl.BLEND)

    def render_pygame(self, time):
        """Render to offscreen surface and copy result into moderngl texture"""
        self.pg_screen.fill((0, 0, 0, 0))  # Make sure we clear with alpha 0!
        pygame.draw.rect(self.pg_screen, (200, 200, 200), [100, 100, 100, 100])

        # Get the buffer view of the Surface's pixels
        # and write this data into the texture
        texture_data = self.pg_screen.get_view('1')
        self.pg_texture.write(texture_data)


if __name__ == '__main__':
    moderngl_window.run_window_config(Pygame, args=('--window', 'pygame2'))
  • Был протестирован на Ubuntu18.04 с Python3 .6
...