Search code examples
pythonopenglpyopengl

OpengL Where is my square?


I cannot figure out why the glDrawElements is not working. I've used the glDrawArrays successfully. But the moment I try to draw with glDrawElements, it is not working.

I am following the learnopengl.com website's tutorials in python, so it adds a bit to the headache. Any idea why this is not working? I've been looking at it for almost a day and can't see the error in my ways.

import glfw
import numpy as np
import OpenGL

from OpenGL.GL import *
from OpenGL.GL import shaders, ArrayDatatype
from ctypes import sizeof, c_float, c_void_p

def main():
    width = 800
    height = 600
    title = 'Hello Triangle'

    vertex = """#version 410 core
        in vec3 aPos;
        void main() {
            gl_Position = vec4(aPos, 1.0);
        }"""
    fragment = """#version 410 core
        out vec4 FragColor;
        void main() {
            FragColor = vec4(1.0, 0.5, 0.2, 1.0);
        }"""

    if not glfw.init():
        raise TypeError('Unable to initalize glfw')

    glfw.window_hint(glfw.SAMPLES, 4)
    glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 4)
    glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 1)
    glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
    glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, True)
    glfw.window_hint(glfw.OPENGL_DEBUG_CONTEXT, True)

    window = glfw.create_window(width, height, title, None, None)
    if not window:
        raise TypeError('Unable to create the window')
    glfw.make_context_current(window)
    glfw.set_framebuffer_size_callback(window, framebuffer_size)

    verts = np.array([[-0.5, 0.5, 0.0], [-0.5, -0.5, 0.0], [0.5, -0.5, 0.0], [0.5, 0.5, 0.0]], dtype=np.float)
    indices = np.array([[0, 1, 3], [3, 1, 2]], dtype=np.uint)

    vertex_shader = glCreateShader(GL_VERTEX_SHADER)
    glShaderSource(vertex_shader, vertex)
    glCompileShader(vertex_shader)

    fragment_shader = glCreateShader(GL_FRAGMENT_SHADER)
    glShaderSource(fragment_shader, fragment)
    glCompileShader(fragment_shader)

    shader_program = glCreateProgram()
    glAttachShader(shader_program, vertex_shader)
    glAttachShader(shader_program, fragment_shader)
    glLinkProgram(shader_program)

    glDeleteShader(vertex_shader)
    glDeleteShader(fragment_shader)

    vao_id = glGenVertexArrays(1)
    vbo_id = glGenBuffers(1)
    ebo_id = glGenBuffers(1)

    glBindVertexArray(vao_id)

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo_id)
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW)
    #glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)

    glBindBuffer(GL_ARRAY_BUFFER, vbo_id)
    glBufferData(GL_ARRAY_BUFFER, verts, GL_STATIC_DRAW)
    glVertexAttribPointer(0, 3, GL_FLOAT, False, 0, None)
    glEnableVertexAttribArray(0)

    #glBindBuffer(GL_ARRAY_BUFFER, 0)

    #glBindVertexArray(0)

    while not glfw.window_should_close(window):
        glClearColor(0.2, 0.3, 0.3, 1.0)
        glClear(GL_COLOR_BUFFER_BIT)

        glUseProgram(shader_program)
        glBindVertexArray(vao_id)
        glEnableVertexAttribArray(0)
        #glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0)
        glBindVertexArray(0)

        glfw.swap_interval(1)
        glfw.swap_buffers(window)
        glfw.poll_events()

    glfw.destroy_window(window)
    glfw.terminate()

def framebuffer_size(window, width, height):
    glViewport(0, 0, width, height)


if __name__ == '__main__':
    main()

Solution

  • There are two tricky issues that need to be addressed in this code, and both of them are subtle and python-specific.

    First, you should replace the 0 in glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0) with None. I suspect this is because PyOpenGL uses ctypes to wrap OpenGL and None is recommended to represent the NULL pointer according to the ctypes documentation. If you need to use offsets for your buffers later, you should look at this answer.

    Second, if you just do step 1 and run the code, you might find the output to not be what you expect:

    enter image description here

    What happened to our orange rectangle? Well, the problem here is with the data we are passing to our buffer objects. If you are on a 64-bit device, the numpy data types (dtype) will be 64-bit too if not fully specified. OpenGL expects 32-bit floats and integers rather than doubles, so we need to use np.float32 instead. I would also use np.uint32 for your index buffer for consistency's sake, but this is not strictly necessary. If we fix your verts and indices initialization statements to this:

    verts = np.array([[-0.5, 0.5, 0.0], [-0.5, -0.5, 0.0], [0.5, -0.5, 0.0], [0.5, 0.5, 0.0]], dtype=np.float32)
    indices = np.array([[0, 1, 3], [3, 1, 2]], dtype=np.uint32)
    

    The results should be more like what you expect:

    enter image description here

    Hope this helps!