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

PyOpenGL着色器绘制多个3D物体失败问题求助

PyOpenGL绘制第二个3D物体失败的修复方案

问题分析

你的代码存在几个关键问题导致第二个物体无法显示:

  1. 未保存并绑定对应VAO:init1和init2创建了各自的VAO,但没有返回这些VAO对象。绘制时必须绑定对应物体的VAO,否则会复用最后一次绑定的VAO数据。
  2. Uniform位置获取错误:绘制第二个物体时,你依然从第一个着色器程序中获取uniform变量的位置,而非当前激活的第二个着色器程序,导致矩阵数据无法正确传入。
  3. 未开启深度测试:缺少深度测试会导致物体渲染时忽略前后遮挡关系,可能让第二个物体被错误覆盖。
  4. 冗余着色器编译:两个物体使用完全相同的着色器代码,重复编译完全没必要,共用一个着色器程序即可。

修复后的代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 05:32:02