openglpyopenglimguipyimgui

How to combine rendering a vertex array object with ImGUI?


Using PyOpenGL with GLU and PyGame.

I have a VAO with a VBO and IBO behind it that provide the vertex and index data for the faces. In addition I have a simple vertex and fragment shader. Things render find.

However, I'd like to add ImGUI to my application. So I did the following:

def main():
    width = 800
    height = 800

    display = (width, height)

    pygame.init()
    pygame.display.set_caption('OpenGL VAO with pygame')
    pygame.display.set_mode(display, pygame.DOUBLEBUF | pygame.OPENGL | pygame.RESIZABLE)
    pygame.display.gl_set_attribute(pygame.GL_CONTEXT_MAJOR_VERSION, 4)
    pygame.display.gl_set_attribute(pygame.GL_CONTEXT_MINOR_VERSION, 1)
    pygame.display.gl_set_attribute(pygame.GL_CONTEXT_PROFILE_MASK, pygame.GL_CONTEXT_PROFILE_CORE)

    imgui.create_context()
    impl = PygameRenderer()
    io = imgui.get_io()
    #io.set_WantCaptureMouse(True)
    io.display_size = width, height

    init()

    clock = pygame.time.Clock()
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            impl.process_event(event)

        render(time=clock, imgui_impl=impl)

        pygame.display.flip()
        pygame.time.wait(10)

if __name__ == '__main__':
    main()

There are two important functions here:

The render() function looks similar to this:

def render(time, imgui_impl):
    glClearColor(0.0, 0.0, 0.0, 0.0)
    glClearDepth(1.0)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    
    glUseProgram(program)
    glBindVertexArray(vao)
    
    fElapsedTime = pygame.time.get_ticks() / 1000.0
    for func in g_instanceList:
        transformMatrix = func(fElapsedTime)
        
        glUniformMatrix4fv(modelToCameraMatrixUnif, 1, GL_FALSE, transformMatrix.transpose())
        glDrawElements(GL_TRIANGLES, len(indexData), GL_UNSIGNED_SHORT, None)
    
    glBindVertexArray(0)
    glUseProgram(0)

    imgui.new_frame()
    imgui.begin('Controls', True)
    changed, strings = imgui.input_text('Path to OBJ', '...', imgui.INPUT_TEXT_READ_ONLY)
    imgui.same_line()
    if imgui.button('Load'):
        print('Loading', strings)
    imgui.end()
    imgui.end_frame()
    imgui.render()
    print(imgui.get_draw_data().valid)
    imgui_impl.render(imgui.get_draw_data())

My problem is that ImGUI doesn't show at all. I checked the validity of the ImGui draw data object

imgui.get_draw_data().valid

and it returns True.

How should I handle the ImGUI data? I do turn of the program (shaders) so the only issue I am thinking of is the VAO and that is somehow affects the rendering of the UI.


Solution

  • I found this post that mentions an issue that appears to be similar to mine. However, the source of the problem for that OP is different. The attempt they made to solve it though worked for me, namely unbind all buffers before calling the ImGui procedures.

    In my original code I am unbinding the VAO and also disabling the shader program. It appears that unbinding the VBO is enough even though I did that for the IBO too:

    def render(time, imgui_impl):
        glClearColor(0.0, 0.0, 0.0, 0.0)
        glClearDepth(1.0)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        
        glUseProgram(program)
        glBindVertexArray(vao)
        
        fElapsedTime = pygame.time.get_ticks() / 1000.0
        for func in g_instanceList:
            transformMatrix = func(fElapsedTime)
            
            glUniformMatrix4fv(modelToCameraMatrixUnif, 1, GL_FALSE, transformMatrix.transpose())
            glDrawElements(GL_TRIANGLES, len(indexData), GL_UNSIGNED_SHORT, None)
        
        glBindVertexArray(0) # Disable the VAO
        glBindBuffer(GL_ARRAY_BUFFER, 0) # Disable the VBO
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) # Disable the IBO (not required)
        glUseProgram(0) # Disable the shader program
    
        imgui.new_frame()
        # Add ImGUI contents
        imgui.end_frame()
        imgui.render()
        imgui_impl.render(imgui.get_draw_data())