Search code examples
pythonopenglpython-imaging-librarytexturespyopengl

PyOpenGL and PIL.Image Color Separation Effect When Loading Textures Onto Objects


I've been having this issue for a while and I've ignored it for just as long, but at this point, the fact that it won't go away is making me see that it's a real issue. Whenever I load an image onto an object, the colors separate into bands like this:

Color separation in render

This is the image that I'm using:

What the image should look like

I'm not entirely sure if this is correct, but I've noticed that it only seems to happen on images with complexity. When I have an image that's a single color, it works fine, but textures and photographs don't seem to ever work. Also, note that I tried converting the original image to different file-types to see if that would help with no difference in results.

the functions that I'm using to load the image:

def load_image(filename):
    im = Image.open(filename)
    return im

def get_texture_data(mode, im):
    if mode == "RGB":
        data = im.tobytes("raw", "RGBX", 0, -1)
    elif mode == "RGBA":
        data = im.tobytes("raw", "RGBA", 0, -1)
    return data

And how I load the texture:

texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texture)

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)

tex = load_image(file)
mode = "".join(Image.Image.getbands(tex))
if mode == "RGB":
    data = get_texture_data("RGB", tex)
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex.width, tex.height, 0, GL_RGB, GL_UNSIGNED_BYTE, data)
    glGenerateMipmap(GL_TEXTURE_2D)
else:
    data = get_texture_data("RGBA", tex)
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.width, tex.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
    glGenerateMipmap(GL_TEXTURE_2D)
del data, tex
number = len(self.textures)
r = Texture(texture, number)
self.textures.append(r)
return r

Note that the "Texture" class used is just a simple class which stores the texture id and the number for ease of access.

EDIT: I can also use images that I make from scratch in Paint or GIMP. Even if they're more complex images.

This testing one works as it should:

Functional Test Image


Solution

  • When the bitmap is decoded by the mode "RGB", then each pixel has still a size of 4 bytes, but the alpha channel is undefined ("RGBX").
    Use a format of GL_RGBA and an internal target format for the texture image of GL_RGB, to deal with that:

    if mode == "RGB":
        data = get_texture_data("RGB", tex)
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex.width, tex.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
        # [...]