You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python+Pygame环境下实现OpenGL深度测试?

OpenGL深度测试在Pygame窗口中失效的问题

我在Python中使用OpenGL,因未成功安装GLUT而采用Pygame创建窗口,窗口可正常工作,但深度测试无法正常运行:

  • 仅启用glEnable(GL_DEPTH_TEST)会导致屏幕空白
  • 编写片段着色器显示像素深度值时,所有物体始终显示纯白色(无论如何移动),而非预期的灰度效果,背景为黑色
  • 了解到使用GLUT时需在显示模式中请求深度缓冲区,但未找到Pygame中的实现方法

以下是最小复现代码:

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()

注:可修改第7行的显示分辨率;第89行被注释,否则屏幕空白;移除第94行可停用着色器,此时可见后方立方体绘制在前方立方体之上。


修复方案

1. 为Pygame窗口请求深度缓冲区

Pygame中需要显式设置深度缓冲区大小,在创建窗口前调用pygame.display.gl_set_attribute:

pygame.init()
# 设置24位深度缓冲区
pygame.display.gl_set_attribute(pygame.GL_DEPTH_SIZE, 24)
pygame.display.set_mode(display, DOUBLEBUF | OPENGL | HWSURFACE | FULLSCREEN)

2. 修正透视投影的近裁剪面

gluPerspective的近裁剪面参数不能设为0.0,这会导致深度值计算异常,改为一个极小的正数(如0.1);同时将远裁剪面从10.0改为20.0,确保第二个立方体(z=-10)在视锥体范围内不会被裁剪:

gluPerspective(70, (display[0] / display[1]), 0.1, 20.0)

3. 启用深度测试

取消第89行的注释,启用深度测试:

glEnable(GL_DEPTH_TEST)

4. (可选)优化深度值显示(非线性校正)

透视投影下gl_FragCoord.z是非线性的,近处物体深度值会快速趋近于1(白色),若要得到线性的灰度效果,可在片段着色器中添加校正:

#version 330 core
out vec4 fragColor;

uniform float zNear;
uniform float zFar;

void main()
{             
    // 将非线性z值转换为线性的[0,1]范围
    float linearDepth = (2.0 * zNear) / (zFar + zNear - gl_FragCoord.z * (zFar - zNear));
    fragColor = vec4(vec3(linearDepth), 1.0);
}

然后在Python代码中传递zNear和zFar的uniform值:

def setup_perspective_projection():
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    zNear = 0.1
    zFar = 20.0
    gluPerspective(70, (display[0] / display[1]), zNear, zFar)
    # 传递uniform给着色器
    shader_program = glGetIntegerv(GL_CURRENT_PROGRAM)
    zNear_loc = glGetUniformLocation(shader_program, "zNear")
    zFar_loc = glGetUniformLocation(shader_program, "zFar")
    glUniform1f(zNear_loc, zNear)
    glUniform1f(zFar_loc, zFar)
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

内容的提问来源于stack exchange,提问作者GamingX

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 12:58:10