My texture is being displayed as completely black in pyOpenGL

Question:

I was trying to display a texture in pyOpenGL with the following methods. Instead of the texture being rendered correctly, the entire object was black. The texture is being loaded with PIL in an RGBA format.

I try to load in the texture using this method:

@classmethod
def load_texture(cls, file_name: str):
    try:
        img = Image.open(f"{sys.path[0]}/res/{file_name}.png")
    except Exception as e:
        print(e)
        raise SystemExit
    try:
        ix, iy, image = img.size[0], img.size[1], img.tobytes("raw", "RGBA", 0, -1)
    except SystemError:
        ix, iy, image = img.size[0], img.size[1], img.tobytes("raw", "RGBX", 0, -1)
    texture_id = glGenTextures(1)             # generate a texture texture_id
    cls.__textures.append(texture_id)
    glBindTexture(GL_TEXTURE_2D, texture_id)  # make it current
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
    # copy the texture into the current texture texture_id
    glTexImage2D(GL_TEXTURE_2D, 0, 3, ix, iy, 0, GL_RGBA, GL_UNSIGNED_BYTE, image)
    return texture_id

And display it with this method:

@staticmethod
def render(model: TexturedModel):
    raw_model = model.get_raw_model()
    glBindVertexArray(raw_model.get_vao_id())                    # bind the desired VAO to be able to use it
    glEnableVertexAttribArray(0)                             # we have put the indices in the 0th address
    glEnableVertexAttribArray(1)                             # we have put the textures in the 1st address
    glActiveTexture(GL_TEXTURE0)
    glBindTexture(GL_TEXTURE_2D, model.get_texture().get_id())
    glDrawElements(GL_TRIANGLES, raw_model.get_vertex_count(), GL_UNSIGNED_INT, None)
    glDisableVertexAttribArray(0)                            # disable the attributeList after using it
    glDisableVertexAttribArray(1)                            # disable the attributeList after using it
    glBindVertexArray(0)                                     # unbind the VAO

What exactly is going wrong? I suppose the texture isn’t getting loaded in right as it’s displayed black.

Edit 1: I think it could also be something wrong with the shaders so here is my:

Edit 2: It was not because of the shaders.

shader_program.py:

from OpenGL.GL import *
from OpenGL.GLUT import *
from abc import abstractmethod  # for abstract methods


class ShaderProgram:
    """
    Class for loading and linking all shaders to the program.
    """
    def __init__(self, vertex_file: str, fragment_file: str):
        self.__vertex_shader_id = self.load_shader(vertex_file, GL_VERTEX_SHADER)
        self.__fragment_shader_id = self.load_shader(fragment_file, GL_FRAGMENT_SHADER)
        self.__program_id = glCreateProgram()                           # create program
        glAttachShader(self.__program_id, self.__vertex_shader_id)      # attach the shader to the program
        glAttachShader(self.__program_id, self.__fragment_shader_id)    # attach the shader to the program
        self.bind_attributes()
        glLinkProgram(self.__program_id)                                # link the program to the shaders
        glValidateProgram(self.__program_id)                            # validate the program

    def start(self):
        glUseProgram(self.get_program_id())

    @staticmethod
    def stop():
        glUseProgram(0)

    def clean_up(self):
        self.stop()
        glDetachShader(self.get_program_id(), self.get_vertex_shader_id())
        glDetachShader(self.get_program_id(), self.get_fragment_shader_id())
        glDeleteShader(self.get_vertex_shader_id())
        glDeleteShader(self.get_fragment_shader_id())
        glDeleteProgram(self.get_program_id())

    @abstractmethod
    def bind_attributes(self):
        pass

    def bind_attribute(self, attribute: int, variable_name: str):
        glBindAttribLocation(self.get_program_id(), attribute, variable_name)

    @staticmethod
    def load_shader(file: str, shader_type: int):
        try:
            shader_file = open(file, 'r')                      # read file
            shader_source = ''.join(shader_file.readlines())   # create a continuous string
        except Exception as e:
            print(e)                                        # print exception
            raise SystemExit
        shader_id = glCreateShader(shader_type)             # create the shader
        glShaderSource(shader_id, shader_source)            # load the shader source code
        glCompileShader(shader_id)                          # compile the shader
        if glGetShaderiv(shader_id, GL_COMPILE_STATUS) == GL_FALSE:
            print(glGetShaderInfoLog(shader_id))       # print the info log if it didn't compile correctly
            print("Could not compile shader.")
            raise SystemExit
        return shader_id

    def get_program_id(self):
        return self.__program_id

    def get_vertex_shader_id(self):
        return self.__vertex_shader_id

    def get_fragment_shader_id(self):
        return self.__fragment_shader_id

my static_shader.py:

from .shader_program import ShaderProgram
import sys


class StaticShader(ShaderProgram):
    __VERTEX_FILE = f"{sys.path[0]}/shaders/vertexShader.txt"
    __FRAGMENT_FILE = f"{sys.path[0]}/shaders/fragmentShader.txt"

    def __init__(self):
        super().__init__(self.get_vertex_file(), self.get_fragment_file())

    def bind_attributes(self):
        super().bind_attribute(0, "position")
        super().bind_attribute(1, "texture_coords")

    @classmethod
    def get_vertex_file(cls):
        return cls.__VERTEX_FILE

    @classmethod
    def get_fragment_file(cls):
        return cls.__FRAGMENT_FILE

my fragmentShader.txt:

#version 400 core

in vec2 pass_texture_coords;

out vec4 out_color;

uniform sampler2D texture_sampler;

void main(void){

    out_color = texture(texture_sampler, pass_texture_coords);

}

and my vertexShader.txt:

#version 400 core

in vec3 position;
in vec2 texture_coords;

out vec2 pass_texture_coords;

void main(void){

    gl_Position = vec4(position, 1.0);
    pass_texture_coords = texture_coords;

}
Asked By: Andreas Sabelfeld

||

Answers:

In loader.py I needed to include:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)

in the load_texture method before the glTexImage2D call.

Answered By: Andreas Sabelfeld