Search code examples
pythonopenglpython-imaging-librarytextures

Opengl not rendering texture


I'm trying to render a texture in OpenGL. However, the texture is completely black.

The texture is rendered on a screen-sized quad made of 2 triangles. The triangles are rendered with the triangle fan mode.

It would be really helpful if someone could point out the problem in my code.

Creating the texture:

 texture = gl.glGenTextures(1)
    gl.glActiveTexture(gl.GL_TEXTURE0)
    gl.glBindTexture(gl.GL_TEXTURE_2D, texture)

    gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_NEAREST)
    gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_NEAREST)
    gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, gl.GL_REPEAT);
    gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, gl.GL_REPEAT);

    viewport = gl.glGetIntegerv(gl.GL_VIEWPORT)
    width = viewport[2]
    height = viewport[3]
    
    print(width, height)
    image = Image.open("./madeline.jpg").convert('RGB')
    data = np.array(image).flatten()


    #gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGB, width, height, 0, gl.GL_RGB, gl.GL_FLOAT, None)
    gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGB, 1920, 1080, 0, gl.GL_RGB, gl.GL_UNSIGNED_INT, data)
    gl.glBindTexture(gl.GL_TEXTURE_2D, texture)

vertex data:

# vertex -> x, y, z, u, v
vertices = [

    -1, 1, 0, 0, 1,   # top-left
    1, 1, 0, 1, 1,   # top-right
    1, -1, 0, 1, 0,  # bottom-right
    -1, -1, 0, 0, 0  # bottom-left

    ]

Setting up buffer:

 # setup buffers
    vao = gl.glGenVertexArrays(1)
    vbo = gl.glGenBuffers(1)

    gl.glBindVertexArray(vao)

    gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vbo)
    gl.glBufferData(gl.GL_ARRAY_BUFFER, len(vertices) * floatSize, np.array(vertices, dtype="float32"), gl.GL_STATIC_DRAW)
    
    
    gl.glVertexAttribPointer(0, 3, gl.GL_FLOAT, gl.GL_FALSE, 5 * floatSize, None)
    gl.glVertexAttribPointer(1, 2, gl.GL_FLOAT, gl.GL_FALSE, 5 * floatSize, 3 * floatSize)
    gl.glEnableVertexAttribArray(0)
    gl.glEnableVertexAttribArray(1)

    gl.glBindVertexArray(vao)

vertex shader:

#version 430 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 texCoord;

out vec2 tex;

void main() {

    gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
    tex = texCoord;
}

fragment shader:

#version 430 core


out vec4 fragColour;
in vec2 tex;

uniform sampler2D ourTex;

void main(){

    fragColour = texture(ourTex, tex);
}

main loop:

while not glfwWindowShouldClose(window):
        gl.glClearColor(0.2, 0.3, 0.3, 1.0)
        gl.glClear(gl.GL_COLOR_BUFFER_BIT)
    
    
        #gl.glDrawElements(gl.GL_TRIANGLES, 6, gl.GL_UNSIGNED_INT, 0)
        gl.glDrawArrays(gl.GL_TRIANGLE_FAN, 0, 4)

        glfwSwapBuffers(window)
        glfwPollEvents()

code: https://github.com/RandomPigYT/raytracer


Solution

  • You cannot load the image object directly into a NumPy array. You must retrieve the contents of the image with Image.getdata. The type of the image data is GL_UNSIGNED_BYTE and I suggest using the size attribute of the Image object.

    image = Image.open("./madeline.jpg").convert('RGBA')
    data = np.array(list(image.getdata()), np.uint8)
    width, height = image.size
    gl.glTexImage2D(
        gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, width, height, 0,
        gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, data)
    

    You can also use Image.tobytes to return the image as a bytes object.

    image = Image.open("./madeline.jpg").convert('RGBA')
    data = image.tobytes()
    width, height = image.size
    gl.glTexImage2D(
        gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, width, height, 0,
        gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, data)
    

    The last argument of glVertexAttribPointer is a pointer not an integer:

    gl.glVertexAttribPointer(1, 2, gl.GL_FLOAT, gl.GL_FALSE, 5 * floatSize, 3 * floatSize)

    gl.glVertexAttribPointer(1, 2, gl.GL_FLOAT, gl.GL_FALSE, 5 * floatSize, 
        ctypes.c_void_p(3 * floatSize))