如何在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
相关产品推荐
相关产品推荐

