Search code examples
pythonopenglpygamepyopengl

Getting wrong texture by glBindTexture


I have two textures in .png format (floor and wall), I load them during the initialization, and choose before rendering by glBindTexture, but I get this: screen_shot_1 instead of this: screen_shot_2 Initialization:

def __init__(self, screen_info, wall_number):
    self.screen = screen_info
    self.inscription = font.Font(get_path([RES_PATH[0], 'font', 'main.ttf']), 12)
    self.path = get_path([RES_PATH[0], RES_PATH[1], ''])
    self.sprite_list = {}
    self.tex_list = {}
    self.generate_list()
    self.wall_coordinates = []
    self.wall_number = wall_number
    self.wall_counter = 0

    glClearColor(*BACKGROUND_COLOR)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(FOV, self.screen.current_w / self.screen.current_h, DISTANCE_NEAR, DISTANCE_FAR)
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

    glEnable(GL_DEPTH_TEST)
    glEnable(GL_TEXTURE_2D)
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)

Main loop:

gi = interaction.GI(screen_info, 11)

while True:
    timer.tick(60)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()

    camera.update_view()
    camera.update_move(forward, backward, left, right, gi.get_wall_coordinates())

    gi.draw_ground(0, 0)
    gi.draw_square(-2, 1.5, -4)
    gi.draw_square(0, 1.5, -4)
    gi.draw_square(2, 1.5, -4)
    gi.draw_square(4, 1.5, -4, rot=True)
    gi.draw_square(4, 1.5, -2, rot=True)
    gi.draw_square(4, 1.5, 0, rot=True)
    gi.draw_square(2, 1.5, 2)
    gi.draw_square(0, 1.5, 2)
    gi.draw_square(-2, 1.5, 2)
    gi.draw_square(-2, 1.5, 0, rot=True)
    gi.draw_square(-2, 1.5, -4, rot=True)

    pygame.display.flip()

Texture loader:

def load_tex(self, filename, rotate=False, text_render=False, text=''):
    if not text_render:
        surface = image.load(filename)

        if rotate:
            surface = transform.rotate(surface, 180)

    else:
        surface = self.inscription.render(str(text), 1, (255, 255, 255), (0, 0, 0))
        surface = transform.scale(surface, (64, 64))
        surface = transform.rotate(surface, 180)

    size = surface.get_size()
    surface = image.tostring(surface, 'RGB', True)
    texture = glGenTextures(1)

    glBindTexture(GL_TEXTURE_2D, texture)
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL)
    glTexImage2D(GL_TEXTURE_2D, 0, 3, size[0], size[1], 0, GL_RGB, GL_UNSIGNED_BYTE, surface)

    return texture

Loading texture during the initialization:

def generate_list(self):
    for filename in listdir(self.path):
        self.tex_list[filename] = self.load_tex(get_path([RES_PATH[0], RES_PATH[1], filename]))

Drawing ground:

def draw_ground(self, x, z, length=50):
    glBindTexture(GL_TEXTURE_2D, self.tex_list['floor.png'])
    # glBindTexture(GL_TEXTURE_2D, self.load_tex(get_path([RES_PATH[0], RES_PATH[1], 'floor.png'])))
    glBegin(GL_QUADS)

    glTexCoord2f(0, length)
    glVertex3f(x - length, -0.5, z + length)
    glTexCoord2f(length, length)
    glVertex3f(x + length, -0.5, z + length)
    glTexCoord2f(length, 0)
    glVertex3f(x + length, -0.5, z - length)
    glTexCoord2f(0, 0)
    glVertex3f(x - length, -0.5, z - length)

    glEnd()

Drawing walls:

def draw_square(self, x, y, z, length=2, rot=False):
    glBindTexture(GL_TEXTURE_2D, self.tex_list['wall.png'])
    # glBindTexture(GL_TEXTURE_2D, self.load_tex(get_path([RES_PATH[0], RES_PATH[1], 'wall.png'])))
    glBegin(GL_QUADS)

    glTexCoord2f(0, 1)
    glVertex3f(x, y, z)
    glTexCoord2f(1, 1)

    if not rot:
        glVertex3f(x + length, y, z)
        glTexCoord2f(1, 0)
        glVertex3f(x + length, y - length, z)

    else:
        glVertex3f(x, y, z + length)
        glTexCoord2f(1, 0)
        glVertex3f(x, y - length, z + length)

    glTexCoord2f(0, 0)
    glVertex3f(x, y - length, z)

    glEnd()

So what is the problem?


Solution

  • These are some things I noticed in your code that may or may not be related to your texturing issues:

    • For your glTexParameterf calls, I saw you used GL_NEAREST. This will lead to pixelated textures. If you are not working with pixel art, you might want to consider GL_LINEAR for your final parameter as this performs bilinear interpolation, removing significant pixelation.
    • I don't think you need the glTexEnvf nor the glPixelStorei calls.
    • As good habit, once you are done using a texture, you should unbind it by calling glBindTexture(GL_TEXTURE_0, 0). OpenGL is all about state, so it is a good idea to return things to how they were after you are done so you aren't surprised later by odd behavior. My guess is that you are not binding the second texture correctly (check the texture ids), resulting in the first texture being used for all calls. Try to reorder the draw calls and see if the texture changes. This is speculation though as I could not run your example code.
    • Your parameters for glTexImage2D look odd. You might want to take a look at the opengl docs as well as their python equivalents. I personally like to use enums like GL_RGBA for formats to make the code clearer.

    Here is a simple demo I made that uses pyopengl and pygame. It renders two 2d squares, each with a different texture. The main while loop and the load_texture function are the important parts:

    import pygame
    from pygame.locals import *
    from OpenGL.GL import *
    import sys
    
    def init_gl():
        window_size = width, height = (550, 400)
        pygame.init()
        pygame.display.set_mode(window_size, OPENGL | DOUBLEBUF)
        glEnable(GL_TEXTURE_2D)
        glMatrixMode(GL_PROJECTION)
        glOrtho(0, width, height, 0, -1, 1)
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
    
    def load_texture(texture_url):
        tex_id = glGenTextures(1)
        tex = pygame.image.load(texture_url)
        tex_surface = pygame.image.tostring(tex, 'RGBA')
        tex_width, tex_height = tex.get_size()
        glBindTexture(GL_TEXTURE_2D, tex_id)
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_width, tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex_surface)
        glBindTexture(GL_TEXTURE_2D, 0)
        return tex_id
    
    if __name__ == "__main__":
        init_gl()
        texture1 = load_texture("texture1.png")
        texture2 = load_texture("texture2.png")
    
        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    sys.exit()
    
            glClear(GL_COLOR_BUFFER_BIT)
            glBindTexture(GL_TEXTURE_2D, texture1)
            glBegin(GL_QUADS)
            glTexCoord(0, 0)
            glVertex(50, 50, 0)
            glTexCoord(0, 1)
            glVertex(50, 100, 0)
            glTexCoord(1, 1)
            glVertex(100, 100, 0)
            glTexCoord(1, 0)
            glVertex(100, 50, 0)
            glEnd()
            glBindTexture(GL_TEXTURE_2D, 0)
    
            glBindTexture(GL_TEXTURE_2D, texture2)
            glBegin(GL_QUADS)
            glTexCoord(0, 0)
            glVertex(450, 300, 0)
            glTexCoord(0, 1)
            glVertex(450, 350, 0)
            glTexCoord(1, 1)
            glVertex(500, 350, 0)
            glTexCoord(1, 0)
            glVertex(500, 300, 0)
            glEnd()
            glBindTexture(GL_TEXTURE_2D, 0)
    
            pygame.display.flip()
    

    As a side note, most of both your code and the demo code I have posted is deprecated in modern OpenGL, which does not support slow immediate mode calls like glBegin and glEnd. If you run into performance issues later on, that might be something to consider refactoring to use more modern techniques, like VBOs (Vertex Buffer Objects). But for simple applications, this should not be too much of an issue. Let me know if this helps!