'PyOpenGL texture isn't displaying correctly

I was trying to put texture onto quad with this code:

import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
from PIL import Image
from numpy import array

def loadTexture(texture):
    try:
        text = Image.open(texture)
    except IOError as ex:
        print("Failed to open texture file: ", texture)
        text = Image.open("0.png")

    textData = array(list(text.getdata()))
    textID = glGenTextures(1)
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
    glBindTexture(GL_TEXTURE_2D, textID)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0)
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, text.size[0], text.size[1], 0, GL_RGB, GL_UNSIGNED_BYTE, textData)
    text.close()
    return textID

def drawQuad(centerX, centerY, textureID):
    verts = (
        (centerX+1,centerY+1),
        (centerX+1,centerY-1),
        (centerX-1,centerY-1),
        (centerX-1,centerY+1)
    )

    surf = (0, 1, 2, 3)

    glEnable(GL_TEXTURE_2D)
    glBindTexture(GL_TEXTURE_2D, textureID)

    glBegin(GL_QUADS)
    for vert in surf:
        glVertex2f(verts[vert][0],verts[vert][1])
        glTexCoord2f(verts[vert][0],verts[vert][1])
    glEnd()
    glDisable(GL_TEXTURE_2D)

def main():
    pygame.init()
    disp = (800,800)
    pygame.display.set_mode(disp, DOUBLEBUF|OPENGL)
    gluPerspective(45, (disp[0] / disp[1]), 0.1, 50.0)
    glTranslatef(0.0, 0.0, -5)
    textID = loadTexture("0.png")

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        drawQuad(0, 0, textID)
        pygame.display.flip()
        pygame.time.wait(10)

but after loading texture and running drawQuad() in a loop I've got this mess on screen:

https://i.stack.imgur.com/5UMCf.png (can't embed image directly due to silly reputation system)

If that matters, texture file format is .png and size is 32x32



Solution 1:[1]

See glVertex:

[...] The current color, normal, texture coordinates, and fog coordinate are associated with the vertex when glVertex is called.

Therefore, you need to specify the texture coordinate before specifying the vertex coordinate.
The texture coordinates have to be in range [0.0, 1.0] (see How do opengl texture coordinates work?):

def drawQuad(centerX, centerY, textureID):
    verts = ((1, 1), (1,-1), (-1,-1), (-1,1))
    texts = ((1, 0), (1, 1), (0, 1), (0, 0))
    surf = (0, 1, 2, 3)

    glEnable(GL_TEXTURE_2D)
    glBindTexture(GL_TEXTURE_2D, textureID)

    glBegin(GL_QUADS)
    for i in surf:
        glTexCoord2f(texts[i][0], texts[i][1])
        glVertex2f(centerX + verts[i][0], centerY + verts[i][1])
    glEnd()
    
    glDisable(GL_TEXTURE_2D)

A PNG (Portable Network Graphics) images consist of 4 channels, the 3 color channels and the alpha channel. Ther for the image format needs to be GL_RGBA instead of GL_RGB:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, text.size[0], text.size[1], 0, GL_RGB, GL_UNSIGNED_BYTE, textData)

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, text.size[0], text.size[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, textData)

Complete example with the following image

import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
from PIL import Image
from numpy import array

def loadTexture(texture):
    try:
        text = Image.open(texture)
    except IOError as ex:
        print("Failed to open texture file: ", texture)
        text = Image.open("0.png")

    textData = array(list(text.getdata()))
    textID = glGenTextures(1)
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
    glBindTexture(GL_TEXTURE_2D, textID)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0)
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, text.size[0], text.size[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, textData)
    text.close()
    return textID

def drawQuad(centerX, centerY, textureID):
    verts = ((1, 1), (1,-1), (-1,-1), (-1,1))
    texts = ((1, 0), (1, 1), (0, 1), (0, 0))
    surf = (0, 1, 2, 3)

    glEnable(GL_TEXTURE_2D)
    glBindTexture(GL_TEXTURE_2D, textureID)

    glBegin(GL_QUADS)
    for i in surf:
        glTexCoord2f(texts[i][0], texts[i][1])
        glVertex2f(centerX + verts[i][0], centerY + verts[i][1])
    glEnd()
    
    glDisable(GL_TEXTURE_2D)

def main():
    pygame.init()
    disp = (200, 200)
    pygame.display.set_mode(disp, DOUBLEBUF|OPENGL)
    gluPerspective(45, (disp[0] / disp[1]), 0.1, 50.0)
    glTranslatef(0.0, 0.0, -5)
    textID = loadTexture("0.png")

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        drawQuad(0, 0, textID)
        pygame.display.flip()
        pygame.time.wait(10)
        
main()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1