Question: I have a texture with transparent parts, but instead of being rendered transparent they're black.  To test if the RGBA values get passed on correctly to the shader, I made everything render in greyscale. And as I thought the alpha values weren't getting passed on correctly.  So anyway here is how the textures get loaded:  @classmethod def load_texture(cls, file_name: str): try: img = Image.open(f"{sys.path[0]}/res/{file_name}.png").convert('RGBA') img = img.transpose(Image.FLIP_TOP_BOTTOM) # flip image upside down except Exception as e: print(e) img = Image.open(f"{sys.path[0]}/res/missing_texture.png").convert('RGBA') img = img.transpose(Image.FLIP_TOP_BOTTOM) # flip image upside down ix, iy, image = img.size[0], img.size[1], img.tobytes("raw", "RGBA", 0, -1) texture_id = glGenTextures(1) # generate a texture ID cls.__textures.append(texture_id) glBindTexture(GL_TEXTURE_2D, texture_id) # make it current # copy the texture into the current texture texture_id glTexImage2D(GL_TEXTURE_2D, 0, 3, ix, iy, 0, GL_RGBA, GL_UNSIGNED_BYTE, image) glPixelStorei(GL_UNPACK_ALIGNMENT, 1) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) return texture_id  And this is my fragment shader (the commented out part is obviously not working): #version 400 core in vec2 pass_texture_coords; in vec3 surface_normal; in vec3 to_light_vector; in vec3 to_camera_vector; out vec4 out_color; uniform sampler2D model_texture; uniform vec3 light_color; uniform float shine_damper; uniform float reflectivity; void main(void){ vec4 texture_color = texture(model_texture, pass_texture_coords); texture_color = vec4(vec3(texture_color.a), 1.0); //if(texture_color.a < 0.5){ // discard; //} vec3 unit_normal = normalize(surface_normal); vec3 unit_light_vector = normalize(to_light_vector); float n_dot1 = dot(unit_normal, unit_light_vector); float brightness = max(n_dot1, 0.1); vec3 diffuse = brightness * light_color; vec3 unit_vector_to_camera = normalize(to_camera_vector); vec3 light_direction = -unit_light_vector; vec3 reflected_light_direction = reflect(light_direction, unit_normal); float specular_factor = dot(reflected_light_direction, unit_vector_to_camera); specular_factor = max(specular_factor, 0.0); float damped_factor = pow(specular_factor, shine_damper); vec3 final_specular = damped_factor * reflectivity * light_color; out_color = vec4(diffuse, 1.0) * texture_color + vec4(final_specular, 1.0); }  Also before rendering anything, this method gets called: @staticmethod def update_display(): InputController().apply_input() # apply inputs every frame glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) # so that transparent bits of the texture won't get rendered glEnable(GL_ALPHA_TEST) glEnable(GL_DEPTH_TEST) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # Remove everything from screen (i.e. displays all white) glClearColor(0, 0.3, 0, 1) # set backdrop color glLoadIdentity() # Reset all graphic/shape's position 

Answer: From glTexImage2D: glTexImage2D( GL_TEXTURE_2D, // Target 0, // Level 3, // Internalformat -> 3 -> GL_RGB -> no alpha // Framebuffer does not necessarily // need an alpha channel. // But if you want transparent textures // you have to specify one, // E.g. GL_RGBA (or any other format // from Table 2 with alpha). ix, // Width iy, // Height 0, // Border, must be 0 GL_RGBA, // Format of data GL_UNSIGNED_BYTE, // Type of data image // Data, which gets converted from format // to internalformat. In your case, the // alpha components will be discarded );  Hint: always use symbols, e.g., GL_XXXX. Legacy OpenGL accepts a numeric value for internalformat, but the specification in the link above explicitly mandates one of the symbols from the tables.