PyGameExamplesAndAnswers

StackOverflow            reply.it reply.it


PyGame window and OpenGL context

Related Stack Overflow questions:

A current and valid OpenGL Context are required for any OpenGL statement. You need to set the pygame.OPENGL flag when creating the display Surface:

window = pg.display.set_mode((sw, sh), pygame.DOUBLEBUF | pygame.OPENGL)

If you want to use the Depth Testt, you need to ensure that the default frame buffer has a depth buffer. Set the depth buffer size attribute (GL_DEPTH_SIZE) with pygame.display.gl_set_attribute (Try a size of 24, if that doesn’t work then switch to 16):

pygame.display.gl_set_attribute(GL_DEPTH_SIZE, 24) # <--- set depth buffer size

pygame.display.set_mode((width, height), pygame.DOUBLEBUF| pygame.OPENGL)

glEnable(GL_DEPTH_TEST) # <--- enable depth test

If the display mode is pygame.OPENGL, pygame.display.flip() performs a GL buffer swap.

You need to enable double buffering by setting the pygame.DOUBLEBUF display mode flag (see pygame.display.set_mode()):

screen = pygame.display.set_mode((800, 600), pygame.OPENGL | pygame.DOUBLEBUF)

If you do not want to use double buffering, you must force the execution of GL commands manually with glFlush():

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

    # [...]

    glFlush() # force the execution of GL commands

It is not possible to create an OpenGL Context with an version above 1.0 without any window.
See the answer to the question Creating OpenGL context without window.

However, it is possible to use a completely hidden window for “offscreen” rendering. Unfortunately it is not possible with Pygame to create an initially hidden window. It is only possible to hide a window after it has been created by calling pygame.display.iconify().
See also Hiding pygame display.

However, it is possible to create an initially hidden window with the GLFW library by setting the window hint VISIBLE to False.

Save rendering (Screenshot)

Related Stack Overflow questions:

You can save a pygame.Surface object, object as the Surface associated with the screen with pygame.image.save():

This will save your Surface as either a BMP, TGA, PNG, or JPEG image.

screen = pygame.display.set_mode((w, h))

# [...]

pygame.image.save(screen , "screenshot.jpg")

However, this doesn’t work for pygame.OPENGL Surfaces. You must read the framebuffer with glReadPixels before the display is updated (before pygame.display.flip() or pygame.display.update()). Use pygame.image.fromstring() to create new Surfaces from the buffer. Finally, save the Surface to a file:

screen = pygame.display.set_mode((w, h), pygame.DOUBLEBUF | pygame.OPENGL)

# [...]

size = screen.get_size()
buffer = glReadPixels(0, 0, *size, GL_RGBA, GL_UNSIGNED_BYTE)
pygame.display.flip()

screen_surf = pygame.image.fromstring(buffer, size, "RGBA")
pygame.image.save(screen_surf, "screenshot.jpg")

If the data is in a texture (e.g. stored in a compute shader) you can read it with glGetTexImage:

image_buffer = glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE)
image_surf = pygame.image.fromstring(image_buffer, (width, height), "RGBA")

📁 OpenGL immediate mode - screenshot, glReadPixels
📁 OpenGL immediate mode - store color buffer, glGetTexImage

Mixed drawing

Related Stack Overflow questions:

You can’t do that directly.

However you can draw on a pygame.Surface object with the pygame.draw module or pygame.Surface.blit. Use pygame.PixelArray to access the pixels on the surface directly. Use the pixels to generate an OpenGL Texture object. This texture can be used in OpenGL.

def surfaceToTexture(pygame_surface):
    rgba_surface = pygame.image.tostring(pygame_surface, 'RGBA')
    width, height = pygame_surface.get_size()
    texture_obj = glGenTextures(1)
    glBindTexture(GL_TEXTURE_2D, texture_obj)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba_surface)
    glBindTexture(GL_TEXTURE_2D, 0)
    return texture_obj

image = pygame.image.load('my_image.png')
texture = surfaceToTexture(image)

In the other direction you can render into a OpenGL Renderbuffer or Texture object (see Framebuffers). Load the texture from the GPU with glReadPixels or glGetTexImage and create a pygame.Surface with pygame.image.frombuffer.

size = screen.get_size()
buffer = glReadPixels(0, 0, *size, GL_RGBA, GL_UNSIGNED_BYTE)
pygame.display.flip()
screen_surf = pygame.image.fromstring(buffer, size, "RGBA")
image_buffer = glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE)
image_surf = pygame.image.fromstring(image_buffer, (width, height), "RGBA")