使用 (py)OpenGL 处理纹理和图像

Texture and image handling with (py)OpenGL

我最近开始接触 pygame 并最终接触到 pyOpenGL(仅使用二维),因为在屏幕上绘制图像会出现 pygame 卡顿。但是,我遇到了两个问题,我一生都无法弄清楚如何解决。即处理两个以上的纹理并在加载图像时绕过 pygame 和 OpenGL 的坐标不兼容。

我做了一个简单的 class 叫做 Thingie1:

class Thingie1:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.width = 0
        self.height = 0
        self.sprite = "imageOne.png"
        self.lastsprite = ""
        self.img_data = 0
        self.load()

    def load(self):
        if self.sprite != self.lastsprite:
            im = open(self.sprite)
            self.width, self.height, self.img_data = im.size[0], im.size[1], im.tobytes("raw", "RGB", 0, -1)
            self.lastsprite = self.sprite

            self.Texture = glGenTextures(1)
            glBindTexture(GL_TEXTURE_2D, self.Texture)
            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_LINEAR)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, self.width, self.height, 0, GL_RGB, GL_UNSIGNED_BYTE, self.img_data)
            glEnable(GL_TEXTURE_2D)
        else:
            return

    def draw(self):
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        glTranslate(self.x, self.y, 0)
        glBegin(GL_QUADS)
        glVertex(0, 0, 0)
        glTexCoord2f(0, 0)
        glVertex(self.width, 0, 0)
        glTexCoord2f(0, 1)
        glVertex(self.width, self.height, 0)
        glTexCoord2f(1, 1)
        glVertex(0, self.height, 0)
        glTexCoord2f(1, 0)
        glEnd()
        glFlush()

以及上述 class 的精确副本(目前),称为 Thingie2。 (open() 函数来自 PILLOW 的图像包 (from PIL.Image import open)。)

然后我打电话给

pygame.init()
pygame.display.set_mode((1024,768), pygame.DOUBLEBUF | pygame.OPENGL)

if True:
    glClearColor(*(0,0,0,255))
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluOrtho2D(0, 1024, 768, 0)
    Thing1 = Thingie1(200,200)
    Thing2 = Thingie2(0,0)

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

        Thing2.load()
        Thing1.load()

        Thing2.draw()
        Thing1.draw()
        pygame.display.flip()

Thing2 之上绘制 Thing1,部分有效;以前,当 load()draw() 函数合并为一个时,它可以正确地对对象进行纹理处理,但对我的计算机来说并不是很好。现在,它使用 Thing2 的纹理对两个 class 个实例进行纹理处理,这是第一个问题。

第二个问题,绘制的图片旋转了180°。我试过使用 matplotlib、cv2 和其他包来加载图像,但我最接近解决这个问题的时候是我用 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, self.width, self.height, 0, GL_RGB, GL_UNSIGNED_BYTE, self.img_data[::-1]) 修改了 glTexImage2D 行(使用 PIL.Image open()), 但是这会以灰色+蓝色比例绘制图像 (?)。

那么,首先,pyOpenGL 通常如何处理纹理?我希望这两种纹理都存储在它们各自的 self.Texture 中。问题是他们都绑定到 GL_TEXTURE_2D 吗?我将如何区分这两种纹理?

其次,有没有更好的加载图片的方法或者有没有办法格式化图片像素字节数组的方式,这样就可以很容易地反转(这也适用于 RGBA 图像)?

请注意,glBegin/glEnd sequences and the fixed function matrix stack is deprecated since decades. See Fixed Function Pipeline and Legacy OpenGL 绘制的那幅画。 阅读 Vertex Specification and Shader 了解最先进的渲染方式。


Now, it textures both class instances with Thing2's texture, which is the first issue.

二维纹理由 glEnable(GL_TEXTURE_2D) 启用,可由 glDisable(GL_TEXTURE_2D) 禁用。
如果启用纹理,则应用 glBegin/glEnd 序列绘制几何时当前绑定的纹理。

在绘制几何图形之前必须绑定适当的纹理:

def draw(self):

    # [...]

    glEnable(GL_TEXTURE_2D)
    glBindTexture(GL_TEXTURE_2D, self.Texture)
    glBegin()

    # [...]     

    glEnd()
    glDisable(GL_TEXTURE_2D)

The second issue is, that the drawn image is rotated by 180°

glVertex is called, the the current texture coordinates are associated with the vertex coordinate. The texture coordinate is set by glTexCoord2f.

这意味着 glTexCoord2f has to be done before glVertex

glBegin(GL_QUADS)

glTexCoord2f(0, 0)
glVertex(0, 0, 0)

glTexCoord2f(0, 1)
glVertex(self.width, 0, 0)

glTexCoord2f(1, 1)
glVertex(self.width, self.height, 0)

glTexCoord2f(1, 0)
glVertex(0, self.height, 0)

glEnd()