pythonopengl3d2dhud

Python OpenGL - Issues displaying 2d graphics over a 3d scene


My goal is to display a static triangle over a rotated cube using PyOpenGL. However, I had no luck in achieving the result I wanted; instead, I am left with a blank screen. I had tested both the cube and triangle with their corresponding matrix projections on separate files to make sure that the scene itself wasn't the problem. And sure enough, it wasn't.

Triangle Script

Displays a triangle at the bottom left corner of the screen.

def reshape(width, height):

    glViewport(0, 0, width, height)
    glClearColor(0.0, 0.0, 0.0, 0.0)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    glOrtho(0, width, 0, height, 0, 0.1)
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

#--- func for defining triangle
def drawTriangle():

    ### clear color and depth buffer
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()

    ### draw triangle
    glBegin(GL_TRIANGLES)

    glColor3f(0.0, 1.0, 0.0)
    glVertex2f(0, 0)
    glVertex2f(100, 0)
    glVertex2f(0, 100)

    glEnd()
    glutSwapBuffers()

#--- func for main loop
def main():

    window = 0

    glutInit(sys.argv)
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE)
    glutInitWindowSize(640, 480)
    glutInitWindowPosition(200, 200)

    window = glutCreateWindow(b'OpenGL Python Triangle')

    glutReshapeFunc(reshape)
    glutDisplayFunc(drawTriangle)
    glutIdleFunc(drawTriangle)
    glutMainLoop()

if __name__ == '__main__':
    main()

Cube Script

Displays a cube at the center of the screen.

def reshape(width, height): 

        glClearColor(0.0, 0.0, 0.0, 0.0)
        glClearDepth(1.0) 
        glDepthMask(GL_TRUE)
        glDepthFunc(GL_LESS)
        glEnable(GL_DEPTH_TEST)
        glShadeModel(GL_SMOOTH)   
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluPerspective(45.0, float(width)/float(height), 0.1, 100.0)
        glMatrixMode(GL_MODELVIEW)

#--- func for defining cube
def drawCube():

        ### clear color and depth buffer
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

        ### set position and rotation
        glLoadIdentity()
        glTranslatef(0.0,0.0,-6.0)
        glRotatef(30,1.0,0.0,0.0)
        glRotatef(0,0.0,1.0,0.0)
        glRotatef(30,0.0,0.0,1.0)

        ### draw cube
        glBegin(GL_QUADS)

        #/* Long list of vertices

        glEnd()
        glutSwapBuffers()

#--- func for main loop
def main():

        window = 0

        glutInit(sys.argv)
        glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
        glutInitWindowSize(640,480)
        glutInitWindowPosition(200,200)

        window = glutCreateWindow(b'OpenGL Python Cube')

        glutReshapeFunc(reshape)
        glutDisplayFunc(drawCube)
        glutIdleFunc(drawCube)
        glutMainLoop()

if __name__ == "__main__":
        main()

I believe the problem might be occurring with the depth test but I was unsuccessful in providing any results that proved the idea right. Instead, it might be with organizing the matrix projections. I ordered it so that the 3d scene would render first and then the 2d scene would overlay it.

HUD Script

#--- 3D projection function
def render3d():

        global width, height

        ### clear buffers
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glViewport(0, 0, width, height)

        ### projection mode
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluPerspective(45.0, float(width)/float(height), 0.1, 100.0)

        ### modelview mode
        glMatrixMode(GL_MODELVIEW)

        ### setup 3d shader
        glDepthFunc(GL_LESS)
        glEnable(GL_DEPTH_TEST)
        glShadeModel(GL_SMOOTH)

#--- 2D projection function
def render2d():

        global width, height

        ### projection mode
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluOrtho2D(0.0, width, height, 0.0)

        ### modelview mode
        glMatrixMode(GL_MODELVIEW)

        ### setup 2d shader
        glDisable(GL_DEPTH_TEST)

#--- reshape function
def reshape(new_width, new_height):

        global width, height

        ### apply new window size
        width = new_width
        height = new_height

#--- 3D cube function
def scene3d():

        ### setup polygon location & orientation
        glTranslatef(0.0, 0.0, -6.0)
        glRotatef(30, 1.0, 0.0, 0.0)
        glRotatef(0, 1.0, 1.0, 0.0)
        glRotatef(30, 0.0, 0.0, 1.0)

        ### draw cube
        glBegin(GL_QUADS)

        glColor3f(0.0,1.0,0.0)
        glVertex3f( 1.0, 1.0,-1.0)
        glVertex3f(-1.0, 1.0,-1.0)
        glVertex3f(-1.0, 1.0, 1.0)
        glVertex3f( 1.0, 1.0, 1.0) 

        glColor3f(1.0,0.0,0.0)
        glVertex3f( 1.0,-1.0, 1.0)
        glVertex3f(-1.0,-1.0, 1.0)
        glVertex3f(-1.0,-1.0,-1.0)
        glVertex3f( 1.0,-1.0,-1.0) 

        glColor3f(0.0,1.0,1.0)
        glVertex3f( 1.0, 1.0, 1.0)
        glVertex3f(-1.0, 1.0, 1.0)
        glVertex3f(-1.0,-1.0, 1.0)
        glVertex3f( 1.0,-1.0, 1.0)

        glColor3f(1.0,1.0,0.0)
        glVertex3f( 1.0,-1.0,-1.0)
        glVertex3f(-1.0,-1.0,-1.0)
        glVertex3f(-1.0, 1.0,-1.0)
        glVertex3f( 1.0, 1.0,-1.0)

        glColor3f(0.0,0.0,1.0)
        glVertex3f(-1.0, 1.0, 1.0) 
        glVertex3f(-1.0, 1.0,-1.0)
        glVertex3f(-1.0,-1.0,-1.0) 
        glVertex3f(-1.0,-1.0, 1.0) 

        glColor3f(1.0,0.0,1.0)
        glVertex3f( 1.0, 1.0,-1.0) 
        glVertex3f( 1.0, 1.0, 1.0)
        glVertex3f( 1.0,-1.0, 1.0)
        glVertex3f( 1.0,-1.0,-1.0)

        glEnd()

#--- 2D triangle function
def scene2d():

        ### draw triangle
        glBegin(GL_TRIANGLES)

        glColor3f(0.0, 1.0, 0.0)
        glVertex2f(0, 0)
        glVertex2f(100, 0)
        glVertex2f(0, 100)

        glEnd()

#--- draw function
def draw():

        ### 3D scene
        render3d()
        scene3d()

        ### 2D scene
        glPushMatrix()

        render2d()
        scene2d()

        glPopMatrix()

        ### swap buffers
        glutSwapBuffers()

#--- main loop function
def main():

        window = 0

        glutInit(sys.argv)
        glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
        glutInitWindowSize(640,480)
        glutInitWindowPosition(200,200)

        window = glutCreateWindow(b'OpenGL Python HUD')

        glutReshapeFunc(reshape)
        glutDisplayFunc(draw)
        glutIdleFunc(draw)
        glutMainLoop()

#--- only run if this is the main module
if __name__ == "__main__":
        main()

To explain, I defined two matrix projection functions: one for the 3d scene (render3d) and the other for the 2d scene (render2d). The next function (reshape) is to obtain the current window size so that I can input it into the proportions for the matrix projections. The following function (scene3d) creates the cube and sets it at the center of the scene while (scene2d) creates the triangle and sets it at the bottom left corner of the scene. The (draw) function puts everything in order in which the 3d scene is rendered first and then the 2d scene [which I nested under a push and pop matrix]. Finally, the main function creates the window and starts the loop.

Feedback would be greatly appreciated. Thank you and have a nice day!


Solution

  • Inside draw() consider this for a second:

    def draw():
        render3d()
        scene3d()
    
        glPushMatrix()
        render2d()
        scene2d()
        glPopMatrix()
    

    Noticing anything missing?

    There should be a glPushMatrix() and glPopMatrix() around the other part as well:

    def draw():
        glPushMatrix()
        render3d()
        scene3d()
        glPopMatrix()
    
        glPushMatrix()
        render2d()
        scene2d()
        glPopMatrix()
    

    The reason you're seeing a black screen is because within scene3d() you're calling glTranslatef(). So everything within milliseconds of starting the application gets translated off-screen.

    Adding the missing glPushMatrix() and glPopMatrix() and you should see this: