テラByteの時代にキロByte

shader又はdemosceneに関係する事

pygameでOpenGLとその他のimageを同居させる

pygameOpenGLをすると全画面を独占される。それだとglutでやる事と変わらない。pygameの特性を活かしきれない。そこでOpenGLをimageとして使う方法をやってみた。imageをつくるのにpycairoというライブラリーも使ってる。細かい説明も厄介なのでコードだけ。

from os import environ
environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1'
import pygame
from OpenGL.GL import *
from OpenGL.WGL import *
import cairo
import numpy
import math
from ctypes import *
user32 = windll.user32

width, height = 512, 512

pygame.init()
pygame.display.set_mode((width, height))
window = pygame.display.get_surface()

background = pygame.Surface((width, height))
background.fill((20,20,20))

# Get OpenGL context
SM_CXSCREEN, SM_CYSCREEN = 0,1
WS_OVERLAPPEDWINDOW = 0xcf0000
PFD_SUPPORT_OPENGL = 32
PFD_DOUBLEBUFFER = 1
xsc, ysc =user32.GetSystemMetrics(SM_CXSCREEN),user32.GetSystemMetrics(SM_CYSCREEN)
hwnd = user32.CreateWindowExA(0,0xC018,0,WS_OVERLAPPEDWINDOW,0,0,xsc,ysc,0,0,0,0)
hdc = user32.GetDC(hwnd)   
user32.SetForegroundWindow(hwnd)
pfd = PIXELFORMATDESCRIPTOR(0,1,PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER,32,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0)
SetPixelFormat(hdc, ChoosePixelFormat(hdc, pfd), pfd)
wglc = wglCreateContext(hdc)
wglMakeCurrent(hdc, wglc)

# OpenGL init
program = glCreateProgram()
shader = glCreateShader(GL_FRAGMENT_SHADER)
glsl = '''
#version 430
out vec4 fragColor;
uniform vec2 resolution;
uniform float time;
void main() {
    vec2 uv = gl_FragCoord.xy/resolution;
    vec3 col = 0.5 + 0.5*cos(time+uv.xyx+vec3(0,2,4));
    fragColor = vec4(col,1);
}
'''
glShaderSource(shader, glsl)
glCompileShader(shader)
if glGetShaderiv(shader, GL_COMPILE_STATUS) != GL_TRUE:
    raise RuntimeError(glGetShaderInfoLog(shader).decode())
glAttachShader(program, shader)
glLinkProgram(program)
glUseProgram(program)

pixels = numpy.zeros((width, height,3), numpy.uint8)
gl_width = width-50
gl_height = height-200
glViewport(0, 0, gl_width, gl_height)
glUniform2f(glGetUniformLocation(program, "resolution"), gl_width, gl_height)

cairo_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 120, 120)

def cairo_draw(surface):
    ctx = cairo.Context(surface)
    ctx.set_source_rgb(0.8, 0.5, 0.1)
    ctx.rectangle(10, 10, 110, 110)
    ctx.fill()
        
fpsClock = pygame.time.Clock()
running = True
while running:
    time = pygame.time.get_ticks()/1000
    
    window.blit(background, (0, 0))
    
    glUniform1f(glGetUniformLocation(program, "time"), time)
    glRects(1, 1, -1, -1)
    glReadPixels(0, 0, pixels.shape[0],pixels.shape[1], GL_RGB, GL_UNSIGNED_BYTE, pixels)
    image = pygame.image.frombuffer(
        pixels.tobytes(),
        pixels.shape[:2],
        'RGB')
    window.blit(image, (25,100), pygame.Rect(0, 0, gl_width, gl_height))
    
    cairo_draw(cairo_surface)
    image = pygame.image.frombuffer(
        numpy.array(cairo_surface.get_data())[..., ::-1].tobytes(),
        (cairo_surface.get_width(),cairo_surface.get_height()),
         'ARGB')
    x= (width-cairo_surface.get_width())/2 + 150*math.sin(time)
    y= (height-cairo_surface.get_height())/2 + 150*math.cos(time)
    window.blit(image, (x,y))
    
    pygame.display.flip()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    fpsClock.tick(30)

# OpenGL finish
wglMakeCurrent(0, 0)
wglDeleteContext(wglc)
user32.ReleaseDC(hwnd, hdc)
user32.PostQuitMessage(0)
user32.DestroyWindow(hwnd)

pygame.quit()   

caption