如何从另一个进程添加到 opengl pygame VBO

How to add to opengl pygame VBOs from another process

问题

我只是想制作像 minecraft 这样的游戏,但我无法从另一个进程添加到 vbo。奇怪的是,日志出现了两次,window 立即关闭。

密码

import pygame, multiprocessing
from OpenGL.GL import *
from ctypes import *

pygame.init ()
screen = pygame.display.set_mode ((800,600), pygame.OPENGL|pygame.DOUBLEBUF, 24)
glViewport (0, 0, 800, 600)
glClearColor (0.0, 0.5, 0.5, 1.0)
glEnableClientState (GL_VERTEX_ARRAY)

vbo = glGenBuffers (1)
glBindBuffer (GL_ARRAY_BUFFER, vbo)

def triangle():
    vertices = [ 0.0, 1.0, 0.0,  0.0, 0.0, 0.0,  1.0, 1.0, 0.0 ]
    glBufferData (GL_ARRAY_BUFFER, len(vertices)*4, (c_float*len(vertices))(*vertices), GL_STATIC_DRAW)

if __name__ == '__main__':
    multiprocessing.Process(target=triangle).start()

    running = True
    while running:

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

        glClear (GL_COLOR_BUFFER_BIT)

        glBindBuffer (GL_ARRAY_BUFFER, vbo)
        glVertexPointer (3, GL_FLOAT, 0, None)

        glDrawArrays (GL_TRIANGLES, 0, 3)

        pygame.display.flip ()

日志

pygame 2.1.0 (SDL 2.0.16, Python 3.8.6)
Hello from the pygame community. https://www.pygame.org/contribute.html
Unable to load numpy_formathandler accelerator from OpenGL_accelerate
pygame 2.1.0 (SDL 2.0.16, Python 3.8.6)
Hello from the pygame community. https://www.pygame.org/contribute.html
Unable to load numpy_formathandler accelerator from OpenGL_accelerate

预期产出

更新

我将代码更改为:

import pygame, threading
from pyglet.gl import *
from OpenGL.GL import *
from OpenGL.WGL import *
from ctypes import *

pygame.init ()
screen = pygame.display.set_mode ((800,600), pygame.OPENGL|pygame.DOUBLEBUF, 24)
glViewport (0, 0, 800, 600)
glClearColor (0.0, 0.5, 0.5, 1.0)
glEnableClientState (GL_VERTEX_ARRAY)

vbo = glGenBuffers (1)
glBindBuffer (GL_ARRAY_BUFFER, vbo)

hwnd = pygame.display.get_wm_info()['window']
context = wglGetCurrentContext()

def triangle(window_handle, context):
    context2 = wglCreateContext(window_handle)
    wglMakeCurrent(window_handle, context2)

    vbo2 = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, vbo2)
    glBufferData(GL_ARRAY_BUFFER, 12, (GLfloat * 12)(0.0, 0.5, 0.0, -0.5, -0.5, 0.0, 0.5, -0.5, 0.0), GL_STATIC_DRAW)
    glVertexPointer(3, GL_FLOAT, 0, None)
    glFlush()

    wglMakeCurrent(None, None)
    wglDeleteContext(context2)

if __name__ == '__main__':
    threading.Thread(target=triangle, args=[hwnd, context]).start()

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

        glClear (GL_COLOR_BUFFER_BIT)

        glBindBuffer (GL_ARRAY_BUFFER, vbo)
        glVertexPointer (3, GL_FLOAT, 0, None)

        glDrawArrays (GL_TRIANGLES, 0, 3)

        pygame.display.flip ()

但现在它只是打开了 window,然后又关闭了! window 突然关闭。它给出了下面提到的错误。但是如果我注释第 28 行并简单地写 triangle(hwnd, context),一切正常。

错误:

Traceback (most recent call last):
  File "C:\Users\...\AppData\Local\Programs\Python\Python38\lib\threading.py", line 932, in _bootstrap_inner
    self.run()
  File "C:\Users\...\AppData\Local\Programs\Python\Python38\lib\threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
  File "f:/Whosebug - Code/vbo_plus_thread.py", line 27, in triangle
    vbo2 = glGenBuffers(1)
  File "src/latebind.pyx", line 39, in OpenGL_accelerate.latebind.LateBind.__call__
  File "src/wrapper.pyx", line 318, in OpenGL_accelerate.wrapper.Wrapper.__call__
  File "src/wrapper.pyx", line 311, in OpenGL_accelerate.wrapper.Wrapper.__call__
  File "src/errorchecker.pyx", line 58, in OpenGL_accelerate.errorchecker._ErrorChecker.glCheckError
OpenGL.error.GLError: GLError(
        err = 1282,
        description = b'invalid operation',
        baseOperation = glGenBuffers,
        pyArgs = (
                1,
                <object object at 0x000001E23B9B8100>,
        ),
        cArgs = (1, array([0], dtype=uint32)),
        cArguments = (1, array([0], dtype=uint32))
)

OpenGL Context 是本地线程。如果你想在另一个线程中使用 OpenGL 上下文,你必须使它成为那里的当前上下文。
上下文一次只能在一个线程中是当前的。当一个线程的上下文成为当前上下文时,它是该线程独有的并被声明,因此它自动不是所有其他线程的当前上下文。如果要在多个线程中使用相同的上下文,则必须锁定使用上下文的部分以确保对上下文的独占访问。这很可能不是您想要的。
如果你想在一个线程中使用缓冲区进行绘图,但同时你想在另一个线程中更改它的内容,你需要2个OpenGL上下文,其中第一个上下文共享第二个上下文。

您的代码还有一些问题:

  • 需要以字节为单位指定缓冲区日期的大小。因此数据的大小是 12*4 而不是 12。参见 glBufferData
  • 顶点属性规范是一种未共享的上下文状态。对象与上下文相关联。所以当上下文被销毁时,对象也被销毁。参见 OpenGL Context
  • Minimal Windowless OpenGL Context Initialization, Windowless OpenGL,
    OpenGL 上下文依赖于 OpenGL window。所以你需要创建一个隐藏的 OpenGL window 来获得第二个具有正确版本的 OpenGL 上下文。我认为 pygame (Pygame 2 is based on SDL2 甚至不可能做到这一点,可能会有解决方案。

使用 GLFW looks as follows. The vertex buffer object is created on the main thread. In the 2nd thread, a hidden OpenGL window is created that shares the context of the main thread. In this Context the buffer object's data store is updated with glBufferSubData 的基本设置:

from OpenGL.GL import *
from OpenGL.GLU import *
import glfw
import threading

if glfw.init() == glfw.FALSE:
    exit()

event = threading.Event()
def shard_context(window, vbo):

    glfw.window_hint(glfw.VISIBLE, glfw.FALSE)
    window2 = glfw.create_window(300, 300, "Window 2", None, window)
    glfw.make_context_current(window2)
    event.set()

    glBindBuffer(GL_ARRAY_BUFFER, vbo)
    glBufferSubData(GL_ARRAY_BUFFER, 0, 12*4, (GLfloat * 12)(0.0, 0.5, 0.0, -0.5, -0.5, 0.0, 0.5, -0.5, 0.0))
    glFlush()

window = glfw.create_window(300, 300, "Window 1", None, None)
glfw.make_context_current(window)

vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, 12 * 4, None, GL_STATIC_DRAW)

glfw.make_context_current(None)
thread = threading.Thread(target=shard_context, args=[window, vbo])
thread.start()
event.wait()
glfw.make_context_current(window)

glEnableClientState(GL_VERTEX_ARRAY)
glVertexPointer(3, GL_FLOAT, 0, None)
  
while not glfw.window_should_close(window):
    
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
   
    glColor3f(1, 0, 0)
    glDrawArrays (GL_TRIANGLES, 0, 3)

    glfw.swap_buffers(window)
    glfw.poll_events()

glfw.terminate()
exit()