PyOpenGL着色器绘制多个3D物体失败问题求助
PyOpenGL绘制第二个3D物体失败的修复方案
问题分析
你的代码存在几个关键问题导致第二个物体无法显示:
- 未保存并绑定对应VAO:
init1和init2创建了各自的VAO,但没有返回这些VAO对象。绘制时必须绑定对应物体的VAO,否则会复用最后一次绑定的VAO数据。 - Uniform位置获取错误:绘制第二个物体时,你依然从第一个着色器程序中获取uniform变量的位置,而非当前激活的第二个着色器程序,导致矩阵数据无法正确传入。
- 未开启深度测试:缺少深度测试会导致物体渲染时忽略前后遮挡关系,可能让第二个物体被错误覆盖。
- 冗余着色器编译:两个物体使用完全相同的着色器代码,重复编译完全没必要,共用一个着色器程序即可。
修复后的代码
from OpenGL.GL import * from OpenGL.GL.shaders import compileProgram, compileShader import numpy as np import pygame from math import sin, cos, tan, radians import ctypes # Vertex Shader vertex_shader = """ #version 330 in vec3 position; in vec3 color; out vec3 newColor; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() { gl_Position = projection * view * model * vec4(position, 1.0); newColor = color; } """ # Fragment Shader fragment_shader = """ #version 330 in vec3 newColor; out vec4 outColor; void main() { outColor = vec4(newColor, 1.0); } """ # Cube 1 vertices and colors vertices = np.array([ # Vertices Colors [-.1, -.1, -.1], [1, 0, 0], [.1, -.1, -.1], [0, 1, 0], [.1, .1, -.1], [0, 0, 1], [-.1, .1, -.1], [1, 1, 0], [-.1, -.1, .1], [0, 1, 1], [.1, -.1, .1], [1, 1, 1], [.1, .1, .1], [0.5, 0.5, 0.5], [-.1, .1, .1], [0.5, 0.5, 0.5], ], dtype=np.float32) indices = np.array([ 0, 1, 2, 2, 3, 0, # Front face 1, 5, 6, 6, 2, 1, # Right face 5, 4, 7, 7, 6, 5, # Back face 4, 0, 3, 3, 7, 4, # Left face 3, 2, 6, 6, 7, 3, # Top face 4, 5, 1, 1, 0, 4, # Bottom face ], dtype=np.uint32) # Cube 2 vertices and colors vertices2 = np.array([ # Vertices Colors [.2, -.1, -.1], [1, 0, 0], [.4, -.1, -.1], [0, 1, 0], [.4, .1, -.1], [0, 0, 1], [.2, .1, -.1], [1, 1, 0], [.2, -.1, .1], [0, 1, 1], [.4, -.1, .1], [1, 1, 1], [.4, .1, .1], [0.5, 0.5, 0.5], [.2, .1, .1], [0.5, 0.5, 0.5], ], dtype=np.float32) indices2 = np.array([ 0, 1, 2, 2, 3, 0, # Front face 1, 5, 6, 6, 2, 1, # Right face 5, 4, 7, 7, 6, 5, # Back face 4, 0, 3, 3, 7, 4, # Left face 3, 2, 6, 6, 7, 3, # Top face 4, 5, 1, 1, 0, 4, # Bottom face ], dtype=np.uint32) def init_object(vertices_data, indices_data): # Create Vertex Array Object (VAO) vao = glGenVertexArrays(1) glBindVertexArray(vao) # Create Vertex Buffer Object (VBO) and Element Buffer Object (EBO) vbo = glGenBuffers(1) glBindBuffer(GL_ARRAY_BUFFER, vbo) glBufferData(GL_ARRAY_BUFFER, vertices_data.nbytes, vertices_data, GL_STATIC_DRAW) ebo = glGenBuffers(1) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo) glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_data.nbytes, indices_data, GL_STATIC_DRAW) # Set attribute pointers stride = 6 * vertices_data.itemsize position_offset = ctypes.c_void_p(0) color_offset = ctypes.c_void_p(3 * vertices_data.itemsize) glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, position_offset) glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, stride, color_offset) glEnableVertexAttribArray(0) glEnableVertexAttribArray(1) # Unbind VAO to avoid accidental modification glBindVertexArray(0) return vao, len(indices_data) def main(): # Initialize Pygame pygame.init() width, height = 800, 600 pygame.display.set_mode((width, height), pygame.OPENGL | pygame.DOUBLEBUF) pygame.display.set_caption("Shader Testing") glViewport(0, 0, width, height) # Enable depth test to handle object occlusion glEnable(GL_DEPTH_TEST) # Compile shader program once (shared by both objects) shader_program = compileProgram(compileShader(vertex_shader, GL_VERTEX_SHADER), compileShader(fragment_shader, GL_FRAGMENT_SHADER)) # Initialize both objects vao1, indices_count1 = init_object(vertices, indices) vao2, indices_count2 = init_object(vertices2, indices2) running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False glClearColor(0.0, 0.0, 0.0, 0.0) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # Define view and projection matrices view = [1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, -3.0, 1.0] aspect_ratio = width / height near = 0.1 far = 100.0 fov = 45.0 top = near * tan(radians(fov) / 2.0) right = top * aspect_ratio projection = ([near / right, 0.0, 0.0, 0.0], [0.0, near / top, 0.0, 0.0], [0.0, 0.0, -(far + near) / (far - near), -1.0], [0.0, 0.0, -(2 * far * near) / (far - near), 0.0]) # Get uniform locations once (outside loop for efficiency) glUseProgram(shader_program) model_loc = glGetUniformLocation(shader_program, "model") view_loc = glGetUniformLocation(shader_program, "view") projection_loc = glGetUniformLocation(shader_program, "projection") # Set view and projection matrices once (they don't change per frame here) glUniformMatrix4fv(view_loc, 1, GL_FALSE, view) glUniformMatrix4fv(projection_loc, 1, GL_FALSE, projection) # Draw first cube angle = pygame.time.get_ticks() / 1000.0 model = ([cos(angle), -sin(angle), 0, 0], [sin(angle), cos(angle), 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]) glUniformMatrix4fv(model_loc, 1, GL_FALSE, model) glBindVertexArray(vao1) glDrawElements(GL_TRIANGLES, indices_count1, GL_UNSIGNED_INT, None) # Draw second cube (use a different model matrix to distinguish) model2 = ([cos(angle), -sin(angle), 0, 0], [sin(angle), cos(angle), 0, 0], [0, 0, 1, 0], [0.3, 0, 0, 1]) # Translate along X-axis glUniformMatrix4fv(model_loc, 1, GL_FALSE, model2) glBindVertexArray(vao2) glDrawElements(GL_TRIANGLES, indices_count2, GL_UNSIGNED_INT, None) pygame.display.flip() pygame.quit() if __name__ == "__main__": main()
关键修改说明
- 封装了
init_object函数,统一处理VAO/VBO/EBO的创建,返回VAO对象和索引数量,避免冗余代码。 - 仅编译一次着色器程序,两个物体共用,提升效率。
- 开启了深度测试
glEnable(GL_DEPTH_TEST),解决遮挡问题。 - 绘制每个物体前绑定对应的VAO,确保使用正确的顶点数据。
- 修正了uniform位置的获取逻辑,在激活着色器后统一获取,避免错误。
- 为第二个物体设置了独立的模型矩阵,使其位置与第一个物体区分开(沿X轴平移)。
内容的提问来源于stack exchange,提问作者eeeeeeeeeeeeeee
相关产品推荐
相关产品推荐

