Search code examples
pythonwindowsopenglpygamedepth-testing

How can i get depth testing to work in OpenGL using Python and Pygame?


I am trying to work with OpenGL in Python. I haven't managed to get depth testing to work. I haven't managed to install GLUT, so i just used Pygame to create the window and it works.

Just activating depth testing (glEnable(GL_DEPTH_TEST)) results in a blank screen. Writing a fragment shader to show the depth value of every pixel results in all the objects being pure white (no matter how close i move to them) instead of greyscale and the background being black as expected.

#version 330 core
out vec4 fragColor;

void main()
{             
    fragColor = vec4(vec3(gl_FragCoord.z), 1.0);
}

I read somewhere that you need to request a depth buffer in the display mode (when using GLUT) but i haven't found a way to do that in Pygame.

This is a minimal version of my code:

import OpenGL
from OpenGL.GL import *
from OpenGL.GLU import *
import pygame
from pygame.locals import *

display = (2560, 1440)

def setup_perspective_projection():
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(70, (display[0] / display[1]), 0.0, 10.0)
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

def draw_3d_cube(translatef_x, translatef_y, translatef_z):
    setup_perspective_projection()

    glTranslatef(translatef_x, translatef_y, translatef_z)

    glBegin(GL_QUADS)
    # Front face
    glColor3f(1.0, 0.0, 0.0)  # Red
    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)
    # Back face
    glColor3f(0.0, 1.0, 0.0)  # Green
    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)
    # Top face
    glColor3f(0.0, 0.0, 1.0)  # Blue
    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)
    # Bottom face
    glColor3f(1.0, 1.0, 0.0)  # Yellow
    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)
    # Right face
    glColor3f(1.0, 0.0, 1.0)  # Magenta
    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)
    # Left face
    glColor3f(0.0, 1.0, 1.0)  # Cyan
    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()

# Shader source code
fragment_shader_source = """
#version 330 core
out vec4 fragColor;

void main()
{             
    fragColor = vec4(vec3(gl_FragCoord.z), 1.0);
}
"""

def compile_shader(shader_type, source):
    shader = glCreateShader(shader_type)
    glShaderSource(shader, source)
    glCompileShader(shader)
    return shader

def create_shader_program(fragment_source):
    fragment_shader = compile_shader(GL_FRAGMENT_SHADER, fragment_source)

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

    return shader_program

def main():
    pygame.init()
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL | HWSURFACE | FULLSCREEN)
    #glEnable(GL_DEPTH_TEST)
    glDepthFunc(GL_LESS)
    glEnable(GL_CULL_FACE)

    shader_program = create_shader_program(fragment_shader_source)
    glUseProgram(shader_program)

    clock = pygame.time.Clock()
    FPS = 144

    while True:
        clock.tick(FPS)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                return

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

        draw_3d_cube(-2, -2, -5)
        draw_3d_cube(-2,-2,-10)

        pygame.display.flip()

if __name__ == "__main__":
    main()

You might want to change the resolution/size of your display in line 7. Line 89 is a comment because else the screen is blank. To deactivate the shader you can just remove line 94 (You can see how the cube in the back is drawn infront of the cube infront).


Solution

  • The problem in your code it the perspective projection. The distance to the near plan must be > 0. See gluPerspective. You can solve that by change near to a small value grater that 0, e.g.: 0.01

    gluPerspective(70, (display[0] / display[1]), 0, 10.0)

    gluPerspective(70, (display[0] / display[1]), 0.01, 10.0)
    

    The perspective projection matrix defines a Viewing frustum, with 0 < near < far. The viewing frustum is limited by the near and far plane. The geometry is clipped at the 6 planes of the frustum. A value of 0 for the near plan leads to undefined behavior and most likely to a division by 0 in the internal calculation. In your case, the depth test does not work, because the calculation of the z and w components of the Homogeneous Clip Space coordinate is incorrect.