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
.
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
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")