如何在pygame中让OpenGL绘制非显示表面?

3
我正在尝试制作一个看起来像复古太空射击游戏的游戏,其中线条是3D线框。为了在Python中实现3D效果,我使用pygame作为窗口,PyOpenGL来渲染对象。
我想让OpenGL渲染到一个表面(而不是显示表面),然后通过pygame将表面缩放并渲染到显示表面上。这将带来一个低分辨率窗口的效果,同时适合现代屏幕。
但是,阻止我这样做的问题是OpenGL渲染到显示表面,我找不到任何选项允许我更改它绘制的表面。
所以这个过程应该是:OpenGL渲染到小表面,pygame对表面进行缩放并将其绘制到显示屏幕上,重复此过程。
以下是我的当前代码:
def main():
    pygame.init()
    display = (500,500)
    pygame.display.set_mode(display, DOUBLEBUF|OPENGL) # Create display window

    gluPerspective(70, (display[0]/display[1]), 0.1, 50.0) # Setup view

    glTranslatef(0.0,0.0, -5) # Set view position

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT: # If X is clicked
                pygame.quit() # Close
                quit()
        glRotatef(1, 3, 1, 1) # Rotates view
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) # Clears the screen
        Cube() # Renders the cube onto the screen
        pygame.display.flip() # Updates the display
        pygame.time.wait(10)

main()

我尝试使用与显示器完全相同的设置创建一个表面,但OpenGL仍然无法将其渲染到该表面上。

1个回答

4

在主循环之前,您需要创建一个分辨率小于窗口分辨率的Framebuffer Object。还可以参考Framebuffer Object扩展示例

fb_size = [50, 50]

depth_buffer_obj = glGenRenderbuffers(1)
glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer_obj)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, fb_size[0], fb_size[1])

color_buffer_obj = glGenRenderbuffers(1)
glBindRenderbuffer(GL_RENDERBUFFER, color_buffer_obj)
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, fb_size[0], fb_size[1])

fb_obj = glGenFramebuffers(1)
glBindFramebuffer(GL_FRAMEBUFFER, fb_obj)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_buffer_obj)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color_buffer_obj)

status = glCheckFramebufferStatus(GL_FRAMEBUFFER)
if status != GL_FRAMEBUFFER_COMPLETE:
    print("incomplete framebuffer object")
glBindFramebuffer(GL_FRAMEBUFFER, 0)

将视口大小设置为帧缓冲区的大小,清除帧缓冲区并将立方体渲染到帧缓冲区

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

    glBindFramebuffer(GL_FRAMEBUFFER, fb_obj)
    glViewport (0, 0, fb_size[0], fb_size[1])
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)

    glRotatef(1, 3, 1, 1)
    Cube()

将视口大小设置为窗口大小,并使用GL_NEAREST过滤器参数的glBlitFramebuffer函数,从命名的帧缓冲对象复制像素到默认帧缓存。请注意,无需清除默认帧缓存,因为它会被完全覆盖:

while True:

    # .....

    glBindFramebuffer(GL_READ_FRAMEBUFFER, fb_obj)
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)
    glViewport(0, 0, 500, 500)

    glBlitFramebuffer(
        0, 0, fb_size[0], fb_size[1],
        0, 0, 500, 500,
        GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
        GL_NEAREST)

    pygame.display.flip()
    pygame.time.wait(10)

请注意,在这一点上,fb_obj已经绑定为读和写,因此不必执行glBindFramebuffer(GL_READ_FRAMEBUFFER, fb_obj)
如果您的系统不支持glBlitFramebuffer,则可以创建一个带有纹理附加到其颜色平面的帧缓冲。
fb_size = [50, 50]

depth_buffer_obj = glGenRenderbuffers(1)
glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer_obj)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, fb_size[0], fb_size[1])

#color_buffer_obj = glGenRenderbuffers(1)
#glBindRenderbuffer(GL_RENDERBUFFER, color_buffer_obj)
#glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, fb_size[0], fb_size[1])

color_tex_obj = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, color_tex_obj)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fb_size[0], fb_size[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, None)

fb_obj = glGenFramebuffers(1)
glBindFramebuffer(GL_FRAMEBUFFER, fb_obj)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_buffer_obj)
#glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color_buffer_obj)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_tex_obj, 0)

status = glCheckFramebufferStatus(GL_FRAMEBUFFER)
if status != GL_FRAMEBUFFER_COMPLETE:
    print("incomplete framebuffer object")
glBindFramebuffer(GL_FRAMEBUFFER, 0)
glBindTexture(GL_TEXTURE_2D, 0) 

将内容渲染到帧缓冲区并在整个窗口上绘制一个四边形纹理到默认帧缓冲区:

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

    glBindFramebuffer(GL_FRAMEBUFFER, fb_obj)
    glViewport (0, 0, fb_size[0], fb_size[1])
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)

    glRotatef(1, 3, 1, 1)
    gluSphere(gluNewQuadric( ), 2.0, 32, 32)

    glBindFramebuffer(GL_FRAMEBUFFER, 0)
    glViewport(0, 0, 500, 500)
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)

    glMatrixMode(GL_PROJECTION)
    glPushMatrix()
    glLoadIdentity()
    glMatrixMode(GL_MODELVIEW)
    glPushMatrix()
    glLoadIdentity()

    #glBlitFramebuffer(
    #    0, 0, fb_size[0], fb_size[1],
    #    0, 0, 500, 500,
    #    GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
    #    GL_NEAREST)

    glEnable(GL_TEXTURE_2D)
    glBindTexture(GL_TEXTURE_2D, color_tex_obj)
    glBegin(GL_TRIANGLE_FAN)
    glTexCoord2f(0,0)
    glVertex2f(-1,-1)
    glTexCoord2f(1,0)
    glVertex2f(1,-1)
    glTexCoord2f(1,1)
    glVertex2f(1,1)
    glTexCoord2f(0,1)
    glVertex2f(-1,1)
    glEnd()
    glDisable(GL_TEXTURE_2D)

    glMatrixMode(GL_PROJECTION)
    glPopMatrix()
    glMatrixMode(GL_MODELVIEW)
    glPopMatrix()

    pygame.display.flip()
    pygame.time.wait(10)

它抛出了一个GLError错误,说第三行到最后一个函数/行的“glBlitFramebuffer”是一个“无效操作”。 - Harry Anthony
我查看了一些不同的网站,它们对于PyOpenGL使用的OpenGL版本有不同的说法。Sourceforge称其使用1.1-4.4版本,但Python维基表示其只使用1.2版本。我打印了GL_VERSION,它显示"GL_VERSION(7938)"。我找不到一种将我得到的数字转换为这些网站所谈论的版本的方法。你知道我正在使用哪个版本吗? - Harry Anthony
3
GL_VERSION是一个枚举类型,你需要将其传递给glGetString()函数。 - genpfault

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接