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

如何在PyGame窗口中用OpenGL 3.3绘制3D立方体?

在Pygame + OpenGL 3.3中显示3D立方体的解决方案

核心问题分析

你目前的核心错误有三个:

  1. 误用图元枚举:GL_IMAGE_CUBE和GL_SAMPLER_CUBE是纹理采样相关的枚举,根本不是用于绘制几何体的图元类型,OpenGL绘制3D物体需要用GL_TRIANGLES、GL_TRIANGLE_STRIP这类图元。
  2. 对核心模式的误解:OpenGL 3.3核心模式完全移除了固定管线,不存在"z≠0自动设置片段着色器"的说法,必须手动编写顶点着色器和片段着色器。
  3. 顶点数据不完整:仅提供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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 16:44:54