如何在PyGame窗口中用OpenGL 3.3绘制3D立方体?
在Pygame + OpenGL 3.3中显示3D立方体的解决方案
核心问题分析
你目前的核心错误有三个:
- 误用图元枚举:
GL_IMAGE_CUBE和GL_SAMPLER_CUBE是纹理采样相关的枚举,根本不是用于绘制几何体的图元类型,OpenGL绘制3D物体需要用GL_TRIANGLES、GL_TRIANGLE_STRIP这类图元。 - 对核心模式的误解:OpenGL 3.3核心模式完全移除了固定管线,不存在"z≠0自动设置片段着色器"的说法,必须手动编写顶点着色器和片段着色器。
- 顶点数据不完整:仅提供8个立方体顶点无法构成立方体的面,需要定义每个面的三角形顶点(或使用索引缓冲EBO优化)。
分步解决方案
1. 规范Pygame窗口初始化(指定OpenGL核心模式)
保持DOUBLEBUF|OPENGL基础参数,额外指定OpenGL 3.3核心模式避免兼容性问题:
import pygame as pg from OpenGL.GL import * import pygame.math as math import ctypes pg.init() display = (800, 600) # 指定OpenGL 3.3核心模式 pg.display.gl_set_attribute(pg.GL_CONTEXT_MAJOR_VERSION, 3) pg.display.gl_set_attribute(pg.GL_CONTEXT_MINOR_VERSION, 3) pg.display.gl_set_attribute(pg.GL_CONTEXT_PROFILE_MASK, pg.GL_CONTEXT_PROFILE_CORE) screen = pg.display.set_mode(display, pg.DOUBLEBUF | pg.OPENGL)
2. 定义完整的立方体顶点数据
每个面用2个三角形(6个顶点)组成,包含位置和颜色信息:
cube_vertices = [ # 前面 0.0, 0.0, 0.5, 1.0, 1.0, 1.0, 0.5, 0.0, 0.5, 1.0, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 0.0, 0.0, 0.5, 1.0, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 0.0, 0.5, 0.5, 1.0, 1.0, 1.0, # 后面 0.0, 0.0, 0.0, 0.8, 0.8, 0.8, 0.5, 0.0, 0.0, 0.8, 0.8, 0.8, 0.5, 0.5, 0.0, 0.8, 0.8, 0.8, 0.0, 0.0, 0.0, 0.8, 0.8, 0.8, 0.5, 0.5, 0.0, 0.8, 0.8, 0.8, 0.0, 0.5, 0.0, 0.8, 0.8, 0.8, # 左面 0.0, 0.0, 0.0, 0.6, 0.6, 0.6, 0.0, 0.0, 0.5, 0.6, 0.6, 0.6, 0.0, 0.5, 0.5, 0.6, 0.6, 0.6, 0.0, 0.0, 0.0, 0.6, 0.6, 0.6, 0.0, 0.5, 0.5, 0.6, 0.6, 0.6, 0.0, 0.5, 0.0, 0.6, 0.6, 0.6, # 右面 0.5, 0.0, 0.0, 0.4, 0.4, 0.4, 0.5, 0.0, 0.5, 0.4, 0.4, 0.4, 0.5, 0.5, 0.5, 0.4, 0.4, 0.4, 0.5, 0.0, 0.0, 0.4, 0.4, 0.4, 0.5, 0.5, 0.5, 0.4, 0.4, 0.4, 0.5, 0.5, 0.0, 0.4, 0.4, 0.4, # 顶面 0.0, 0.5, 0.0, 0.2, 0.2, 0.2, 0.5, 0.5, 0.0, 0.2, 0.2, 0.2, 0.5, 0.5, 0.5, 0.2, 0.2, 0.2, 0.0, 0.5, 0.0, 0.2, 0.2, 0.2, 0.5, 0.5, 0.5, 0.2, 0.2, 0.2, 0.0, 0.5, 0.5, 0.2, 0.2, 0.2, # 底面 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, ]
3. 编写并编译着色器程序
核心模式必须使用自定义着色器,以下是基础的顶点和片段着色器:
def compile_shader(source, shader_type): shader = glCreateShader(shader_type) glShaderSource(shader, source) glCompileShader(shader) if not glGetShaderiv(shader, GL_COMPILE_STATUS): error = glGetShaderInfoLog(shader).decode() raise RuntimeError(f"Shader compile error: {error}") return shader # 顶点着色器 vertex_shader_source = """ #version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aColor; out vec3 ourColor; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() { gl_Position = projection * view * model * vec4(aPos, 1.0); ourColor = aColor; } """ # 片段着色器 fragment_shader_source = """ #version 330 core in vec3 ourColor; out vec4 FragColor; void main() { FragColor = vec4(ourColor, 1.0); } """ # 编译链接着色器程序 vertex_shader = compile_shader(vertex_shader_source, GL_VERTEX_SHADER) fragment_shader = compile_shader(fragment_shader_source, GL_FRAGMENT_SHADER) shader_program = glCreateProgram() glAttachShader(shader_program, vertex_shader) glAttachShader(shader_program, fragment_shader) glLinkProgram(shader_program) if not glGetProgramiv(shader_program, GL_LINK_STATUS): error = glGetProgramInfoLog(shader_program).decode() raise RuntimeError(f"Program link error: {error}") glDeleteShader(vertex_shader) glDeleteShader(fragment_shader)
4. 创建VAO和VBO管理顶点数据
核心模式必须使用VAO来绑定顶点属性:
vao = glGenVertexArrays(1) vbo = glGenBuffers(1) glBindVertexArray(vao) glBindBuffer(GL_ARRAY_BUFFER, vbo) glBufferData(GL_ARRAY_BUFFER, len(cube_vertices)*4, (GLfloat * len(cube_vertices))(*cube_vertices), GL_STATIC_DRAW) # 位置属性:每个顶点3个float,步长6个float(3位置+3颜色) glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), ctypes.c_void_p(0)) glEnableVertexAttribArray(0) # 颜色属性:从第3个float开始,步长6个float glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), ctypes.c_void_p(3 * sizeof(GLfloat))) glEnableVertexAttribArray(1) glBindBuffer(GL_ARRAY_BUFFER, 0) glBindVertexArray(0)
5. 主循环绘制立方体
设置投影、视图矩阵,开启深度测试并绘制:
glEnable(GL_DEPTH_TEST) clock = pg.time.Clock() # 投影矩阵:透视投影 projection = math.Matrix44.perspective_projection(45, display[0]/display[1], 0.1, 100.0) # 视图矩阵:相机位置在(1,1,2),看向原点 view = math.Matrix44.look_at(math.Vector3(1,1,2), math.Vector3(0,0,0), math.Vector3(0,1,0)) running = True while running: for event in pg.event.get(): if event.type == pg.QUIT: running = False glClearColor(0.1, 0.1, 0.1, 1.0) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glUseProgram(shader_program) # 更新模型矩阵(旋转立方体) model = math.Matrix44().rotate_y(pg.time.get_ticks() * 0.001) # 设置uniform变量 glUniformMatrix4fv(glGetUniformLocation(shader_program, "model"), 1, GL_FALSE, model) glUniformMatrix4fv(glGetUniformLocation(shader_program, "view"), 1, GL_FALSE, view) glUniformMatrix4fv(glGetUniformLocation(shader_program, "projection"), 1, GL_FALSE, projection) # 绑定VAO并绘制 glBindVertexArray(vao) glDrawArrays(GL_TRIANGLES, 0, 36) # 6个面×6顶点=36个顶点 glBindVertexArray(0) pg.display.flip() clock.tick(60) # 清理资源 glDeleteVertexArrays(1, [vao]) glDeleteBuffers(1, [vbo]) glDeleteProgram(shader_program) pg.quit()
内容的提问来源于stack exchange,提问作者Root
相关产品推荐
相关产品推荐

