我需要一些有关下面代码的帮助,其中我try 使用Impasse(PyAssimp的一个分支)加载可能包含多个网格的ObJ.为了做到这一点并帮助自己进行调试,我将所有数据存储在字典 struct 中,这使我能够以一种简单的方式获取顶点、 colored颜色 和面部数据.

这两个代码片段都取自Mario Rosasco关于PyGL的教程系列,第一个代码片段经过了大量修改以支持PyGame、ImGUI(我已经删除了所有窗口,因为它对我的问题没有用),从一个ObJ文件加载并支持多个网格.最初的代码使用单个VAO(具有混合顶点和 colored颜色 数据的相应VBO)和IBO,并且它正在工作.根据要求我可以提供.

一旦我开始添加多个缓冲区和数组对象,我就陷入了困境,现在除了场景的黑色背景色之外,我无法渲染任何东西.

我使用的数据 struct 非常简单-一个带有4个键的字典:

  • vbos -字典列表,每个字典都表示网格的顶点
  • cbos -字典列表,每个字典都表示网格的顶点 colored颜色
  • ibos -字典列表,每个字典代表网格所有面的索引
  • vaos -VAO参考文献列表

前三个具有相同的元素 struct ,即:

{
  'buff_id' : <BUFFER OBJECT REFERENCE>,
  'data' : {
    'values' : <BUFFER OBJECT DATA (flattened)>,
    'count' : <NUMBER OF ELEMENTS IN BUFFER OBJECT DATA>,
    'stride' : <DIMENSION OF A SINGLE DATUM IN A BUFFER OBJECT DATA>
   }
}

我假设多网格BOJ(我使用Blender来创建文件)每个网格都有一个单独的顶点等数据,并且每个网格都被定义为g分量.此外,我在输出时使用三角测量(因此目前并不真正需要stride),因此我所有的脸都是基元.

我几乎100%确定我在某个地方错过了绑定/解除绑定,但我找不到那个地方.或者也许我的代码存在一个不同的、更根本的问题.

import sys
from pathlib import Path
import shutil

import numpy as np

from OpenGL.GLU import *
from OpenGL.GL import *

import pygame

import glm
from utils import *
from math import tan, cos, sin, sqrt
from ctypes import c_void_p

from imgui.integrations.pygame import PygameRenderer
import imgui

import impasse
from impasse.constants import ProcessingPreset, MaterialPropertyKey
from impasse.structs import Scene, Camera, Node
from impasse.helper import get_bounding_box

scene_box = None

def loadObjs(f_path: Path):
    if not f_path:
        raise TypeError('f_path not of type Path')
    if not f_path.exists():
        raise ValueError('f_path not a valid filesystem path')
    if not f_path.is_file():
        raise ValueError('f_path not a file')
    if not f_path.name.endswith('.obj'):
        raise ValueError('f_path not an OBJ file')
    
    mtl_pref = 'usemtl '
    mltlib_found = False
    mtl_names = []
    obj_parent_dir = f_path.parent.absolute()
    obj_raw = None
    with open(f_path, 'r') as f_obj:
        obj_raw = f_obj.readlines()
        for line in obj_raw:
            if line == '#':
                continue
            elif line.startswith('mtllib'):
                mltlib_found = True
            elif mtl_pref in line:
                mtl_name = line.replace(mtl_pref, '')
                print('Found material "{}" in OBJ file'.format(mtl_name))
                mtl_names.append(mtl_name)

    args = {}
    args['processing'] = postprocess = ProcessingPreset.TargetRealtime_Fast
    scene = impasse.load(str(f_path), **args).copy_mutable()
    scene_box = (bb_min, bb_max) = get_bounding_box(scene)

    print(len(scene.meshes))

    return scene

def createBuffers(scene: impasse.core.Scene):
    vbos = []
    cbos = []
    ibos = []
    vaos = []

    for mesh in scene.meshes:
        print('Processing mesh "{}"'.format(mesh.name))
        color_rnd = np.array([*np.random.uniform(0.0, 1.0, 3), 1.0], dtype='float32')

        vbo = glGenBuffers(1)
        vertices = np.array(mesh.vertices)
        vbos.append({
            'buff_id' : vbo,
            'data' : {
                'values' : vertices.flatten(),
                'count' : len(vertices),
                'stride' : len(vertices[0])
            }
        })
        glBindBuffer(GL_ARRAY_BUFFER, vbos[-1]['buff_id'])
        glBufferData(
            # PyOpenGL allows for the omission of the size parameter
            GL_ARRAY_BUFFER,
            vertices,
            GL_STATIC_DRAW
        )
        glBindBuffer(GL_ARRAY_BUFFER, 0)

        cbo = glGenBuffers(1)
        print('Random color: {}'.format(color_rnd))
        colors = np.full((len(vertices), 4), color_rnd)
        cbos.append({
            'buff_id' : cbo,
            'data' : {
                'values' : colors.flatten(),
                'count' : len(colors),
                'stride' : len(colors[0])
            }
        })
        glBindBuffer(GL_ARRAY_BUFFER, cbos[-1]['buff_id'])
        glBufferData(
            GL_ARRAY_BUFFER,
            colors,
            GL_STATIC_DRAW
        )
        glBindBuffer(GL_ARRAY_BUFFER, 0)
        
        ibo = glGenBuffers(1)
        indices = np.array(mesh.faces)
        ibos.append({
            'buff_id' : ibo,
            'data' : {
                'values' : indices,
                'count' : len(indices),
                'stride' : len(indices[0])
            }
        })
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibos[-1]['buff_id'])
        glBufferData(
            GL_ELEMENT_ARRAY_BUFFER,
            indices,
            GL_STATIC_DRAW
        )
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)

    # Generate the VAOs. Technically, for same VBO data, a single VAO would suffice
    for mesh_idx in range(len(ibos)):
        vao = glGenVertexArrays(1)
        vaos.append(vao)

        glBindVertexArray(vaos[-1])
        
        vertex_dim = vbos[mesh_idx]['data']['stride']
        print('Mesh vertex dim: {}'.format(vertex_dim))
        glBindBuffer(GL_ARRAY_BUFFER, vbos[mesh_idx]['buff_id'])
        glEnableVertexAttribArray(0)
        glVertexAttribPointer(0, vertex_dim, GL_FLOAT, GL_FALSE, 0, None)

        color_dim = cbos[mesh_idx]['data']['stride']
        print('Mesh color dim: {}'.format(color_dim))
        glBindBuffer(GL_ARRAY_BUFFER, cbos[mesh_idx]['buff_id'])
        glEnableVertexAttribArray(1)
        glVertexAttribPointer(1, color_dim, GL_FLOAT, GL_FALSE, 0, None)
        
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibos[mesh_idx]['buff_id'])
        
        glBindVertexArray(0)

    return {
        'vbos' : vbos,
        'cbos' : cbos,
        'ibos' : ibos,
        'vaos' : vaos
        }

wireframe_enabled = False
transform_selected = 0

# Helper function to calculate the frustum scale. 
# Accepts a field of view (in degrees) and returns the scale factor
def calcFrustumScale(fFovDeg):
    degToRad = 3.14159 * 2.0 / 360.0
    fFovRad = fFovDeg * degToRad
    return 1.0 / tan(fFovRad / 2.0)

# Global variable to represent the compiled shader program, written in GLSL
program = None

# Global variables to store the location of the shader's uniform variables
modelToCameraMatrixUnif = None
cameraToClipMatrixUnif = None

# Global display variables
cameraToClipMatrix = np.zeros((4,4), dtype='float32')
fFrustumScale = calcFrustumScale(45.0)

# Set up the list of shaders, and call functions to compile them
def initializeProgram():
    shaderList = []
    
    shaderList.append(loadShader(GL_VERTEX_SHADER, "PosColorLocalTransform.vert"))
    shaderList.append(loadShader(GL_FRAGMENT_SHADER, "ColorPassthrough.frag"))
    
    global program 
    program = createProgram(shaderList)
    
    for shader in shaderList:
        glDeleteShader(shader)
    
    global modelToCameraMatrixUnif, cameraToClipMatrixUnif
    modelToCameraMatrixUnif = glGetUniformLocation(program, "modelToCameraMatrix")
    cameraToClipMatrixUnif = glGetUniformLocation(program, "cameraToClipMatrix")
    
    fzNear = 1.0
    fzFar = 61.0
    
    global cameraToClipMatrix
    # Note that this and the transformation matrix below are both
    # ROW-MAJOR ordered. Thus, it is necessary to pass a transpose
    # of the matrix to the glUniform assignment function.
    cameraToClipMatrix[0][0] = fFrustumScale
    cameraToClipMatrix[1][1] = fFrustumScale
    cameraToClipMatrix[2][2] = (fzFar + fzNear) / (fzNear - fzFar)
    cameraToClipMatrix[2][3] = -1.0
    cameraToClipMatrix[3][2] = (2 * fzFar * fzNear) / (fzNear - fzFar)
    
    glUseProgram(program)
    glUniformMatrix4fv(cameraToClipMatrixUnif, 1, GL_FALSE, cameraToClipMatrix.transpose())
    glUseProgram(0)


def initializeBuffers():
    global vbo, cbo, ibo

    vbo = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, vbo)
    glBufferData(
        # PyOpenGL allows for the omission of the size parameter
        GL_ARRAY_BUFFER,
        obj['vertices'],
        GL_STATIC_DRAW
    )
    glBindBuffer(GL_ARRAY_BUFFER, 0)

    cbo = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, cbo)
    glBufferData(
        GL_ARRAY_BUFFER,
        obj['colors'],
        GL_STATIC_DRAW
    )
    glBindBuffer(GL_ARRAY_BUFFER, 0)
    
    ibo = glGenBuffers(1)
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo)
    glBufferData(
        GL_ELEMENT_ARRAY_BUFFER,
        obj['faces'],
        GL_STATIC_DRAW
    )
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
    

# Helper functions to return various types of transformation arrays
def calcLerpFactor(fElapsedTime, fLoopDuration):
    fValue = (fElapsedTime % fLoopDuration) / fLoopDuration
    if fValue > 0.5:
        fValue = 1.0 - fValue
    return fValue * 2.0
    

def computeAngleRad(fElapsedTime, fLoopDuration):
    fScale = 3.14159 * 2.0 / fLoopDuration
    fCurrTimeThroughLoop = fElapsedTime % fLoopDuration
    return fCurrTimeThroughLoop * fScale


def rotateY(fElapsedTime, **mouse):
    fAngRad = computeAngleRad(fElapsedTime, 2.0)
    fCos = cos(fAngRad)
    fSin = sin(fAngRad)
    
    newTransform = np.identity(4, dtype='float32')
    newTransform[0][0] = fCos
    newTransform[2][0] = fSin
    newTransform[0][2] = -fSin
    newTransform[2][2] = fCos
    # offset 
    newTransform[0][3] = 0.0 #-5.0
    newTransform[1][3] = 0.0 #5.0
    newTransform[2][3] = mouse['wheel']
    return newTransform

# A list of the helper offset functions.
# Note that this does not require a structure def in python.
# Each function is written to return the complete transform matrix.
g_instanceList = [
    rotateY,
]


# Initialize the OpenGL environment
def init(w, h):

    initializeProgram()

    glEnable(GL_CULL_FACE)
    glCullFace(GL_BACK)
    glFrontFace(GL_CW)
    
    glEnable(GL_DEPTH_TEST)
    glDepthMask(GL_TRUE)
    glDepthFunc(GL_LEQUAL)
    glDepthRange(0.0, 1.0)

    glMatrixMode(GL_PROJECTION)
    print('Scene bounding box:', scene_box)
    gluPerspective(45, (w/h), 0.1, 500)
    #glTranslatef(0, 0, -100)

   
def render(time, imgui_impl, mouse, mem, buffers):
    global wireframe_enabled, transform_selected

    #print(mouse['wheel'])
    
    glClearColor(0.0, 0.0, 0.0, 0.0)
    glClearDepth(1.0)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    
    if wireframe_enabled:
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
    else:
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)

    glUseProgram(program)
    
    fElapsedTime = pygame.time.get_ticks() / 1000.0
    transformMatrix = g_instanceList[transform_selected](fElapsedTime, **mouse)

    glUniformMatrix4fv(modelToCameraMatrixUnif, 1, GL_FALSE, transformMatrix.transpose())
    #glDrawElements(GL_TRIANGLES, len(obj['faces']), GL_UNSIGNED_SHORT, None)
    for vao_idx, vao in enumerate(buffers['vaos']):
        print('Rendering VAO {}'.format(vao_idx))
        print('SHAPE VBO', buffers['vbos'][vao_idx]['data']['values'].shape)
        print('SHAPE CBO', buffers['cbos'][vao_idx]['data']['values'].shape)
        print('SHAPE IBO', buffers['ibos'][vao_idx]['data']['values'].shape)

        print('''Address:
    VBO:\t{}
    CBO:\t{}
    IBO:\t{}
    VAO:\t{}
'''.format(id(buffers['vbos'][vao_idx]), id(buffers['cbos'][vao_idx]), id(buffers['ibos'][vao_idx]), id(vao)))

        glBindVertexArray(vao)
        index_dim = buffers['ibos'][vao_idx]['data']['stride']
        index_count = buffers['ibos'][vao_idx]['data']['count']
        glDrawElements(GL_TRIANGLES, index_count, GL_UNSIGNED_SHORT, None)
        glBindVertexArray(0)

    glUseProgram(0)

    imgui.new_frame()
    # Draw windows
    imgui.end_frame()
    imgui.render()
    imgui_impl.render(imgui.get_draw_data())

            
# Called whenever the window's size changes (including once when the program starts)
def reshape(w, h):
    global cameraToClipMatrix
    cameraToClipMatrix[0][0] = fFrustumScale * (h / float(w))
    cameraToClipMatrix[1][1] = fFrustumScale

    glUseProgram(program)
    glUniformMatrix4fv(cameraToClipMatrixUnif, 1, GL_FALSE, cameraToClipMatrix.transpose())
    glUseProgram(0)
    
    glViewport(0, 0, w, h)
    
def main():
    width = 800
    height = 800

    display = (width, height)

    pygame.init()
    pygame.display.set_caption('OpenGL VAO with pygame')
    pygame.display.set_mode(display, pygame.DOUBLEBUF | pygame.OPENGL | pygame.RESIZABLE)
    pygame.display.gl_set_attribute(pygame.GL_CONTEXT_MAJOR_VERSION, 4)
    pygame.display.gl_set_attribute(pygame.GL_CONTEXT_MINOR_VERSION, 1)
    pygame.display.gl_set_attribute(pygame.GL_CONTEXT_PROFILE_MASK, pygame.GL_CONTEXT_PROFILE_CORE)

    imgui.create_context()
    impl = PygameRenderer()
    io = imgui.get_io()
    #io.set_WantCaptureMouse(True)
    io.display_size = width, height

    #scene = loadObjs(Path('assets/sample0.obj'))
    scene = loadObjs(Path('assets/cubes.obj'))
    #scene = loadObjs(Path('assets/shapes.obj'))
    #scene = loadObjs(Path('assets/wooden_watch_tower/wooden watch tower2.obj'))
    buffers = createBuffers(scene)

    init(width, height)

    wheel_factor = 0.3
    mouse = {
        'pressed' : False,
        'motion' : {
            'curr' : np.array([0, 0]),
            'prev' : np.array([0, 0])
        },
        'pos' : {
            'curr' : np.array([0, 0]),
            'prev' : np.array([0, 0])
        },
        'wheel' : -10
    }
    
    clock = pygame.time.Clock()
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT or (event.type ==  pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
                pygame.quit()
                quit()
            impl.process_event(event)

            if event.type == pygame.MOUSEMOTION:
                if mouse['pressed']:
                    #glRotatef(event.rel[1], 1, 0, 0)
                    #glRotatef(event.rel[0], 0, 1, 0)
                    mouse['motion']['curr'] = [event.rel[1], event.rel[0]]
                    mouse['pos']['curr'] = event.pos
            if event.type == pygame.MOUSEWHEEL:
                mouse['wheel'] += event.y * wheel_factor

        for event in pygame.mouse.get_pressed():
            mouse['pressed'] = pygame.mouse.get_pressed()[0] == 1

        render(time=clock, imgui_impl=impl, mouse=mouse, mem=None, buffers=buffers)

        mouse['motion']['prev'] = mouse['motion']['curr']
        mouse['pos']['prev'] = mouse['pos']['curr']

        pygame.display.flip()
        pygame.time.wait(10)

if __name__ == '__main__':
    main()

其中utils模块定义为

from OpenGL.GLU import *
from OpenGL.GL import *
import os
import sys

# Function that creates and compiles shaders according to the given type (a GL enum value) and 
# shader program (a file containing a GLSL program).
def loadShader(shaderType, shaderFile):
    # check if file exists, get full path name
    strFilename = findFileOrThrow(shaderFile)
    shaderData = None
    with open(strFilename, 'r') as f:
        shaderData = f.read()
    
    shader = glCreateShader(shaderType)
    glShaderSource(shader, shaderData) # note that this is a simpler function call than in C
    
    # This shader compilation is more explicit than the one used in
    # framework.cpp, which relies on a glutil wrapper function.
    # This is made explicit here mainly to decrease dependence on pyOpenGL
    # utilities and wrappers, which docs caution may change in future versions.
    glCompileShader(shader)
    
    status = glGetShaderiv(shader, GL_COMPILE_STATUS)
    if status == GL_FALSE:
        # Note that getting the error log is much simpler in Python than in C/C++
        # and does not require explicit handling of the string buffer
        strInfoLog = glGetShaderInfoLog(shader)
        strShaderType = ""
        if shaderType is GL_VERTEX_SHADER:
            strShaderType = "vertex"
        elif shaderType is GL_GEOMETRY_SHADER:
            strShaderType = "geometry"
        elif shaderType is GL_FRAGMENT_SHADER:
            strShaderType = "fragment"
        
        print("Compilation failure for " + strShaderType + " shader:\n" + strInfoLog)
    
    return shader

# Function that accepts a list of shaders, compiles them, and returns a handle to the compiled program
def createProgram(shaderList):
    program = glCreateProgram()
    
    for shader in shaderList:
        glAttachShader(program, shader)
        
    glLinkProgram(program)
    
    status = glGetProgramiv(program, GL_LINK_STATUS)
    if status == GL_FALSE:
        # Note that getting the error log is much simpler in Python than in C/C++
        # and does not require explicit handling of the string buffer
        strInfoLog = glGetProgramInfoLog(program)
        print("Linker failure: \n" + strInfoLog)
        
    for shader in shaderList:
        glDetachShader(program, shader)
        
    return program
    
    
# Helper function to locate and open the target file (passed in as a string).
# Returns the full path to the file as a string.
def findFileOrThrow(strBasename):
    # Keep constant names in C-style convention, for readability
    # when comparing to C(/C++) code.
    LOCAL_FILE_DIR = "data" + os.sep
    GLOBAL_FILE_DIR = ".." + os.sep + "data" + os.sep
    
    strFilename = LOCAL_FILE_DIR + strBasename
    if os.path.isfile(strFilename):
        return strFilename
        
    strFilename = GLOBAL_FILE_DIR + strBasename
    if os.path.isfile(strFilename):
        return strFilename
        
    raise IOError('Could not find target file ' + strBasename)

推荐答案

也许可以判断您的数据格式.例如当你设置时

indices = np.array(mesh.faces)

这个数组必须有dtype == np.uint16,因为您稍后会用

glDrawElements(..., GL_UNSIGNED_SHORT, ...)

因此,如果您的指数数组有dtype == np.uint32,则需要用GL_UNSIGNED_INT绘制.

也验证点数据的数据类型,它必须是np.float32,因为在调用glVertexAttribPointer时定义了GL_FLOAT

Python相关问答推荐

如果在第一行之前不存在其他条件,如何获得满足口罩条件的第一行?

查找3D数组中沿一个轴的相同值序列的长度(与行程长度编码相关)

使用decorator 自动继承父类

如何在Pygame中绘制右对齐的文本?

在Arrow上迭代的快速方法.Julia中包含3000万行和25列的表

如何让我的Tkinter应用程序适合整个窗口,无论大小如何?

Pandas :多索引组

跟踪我已从数组中 Select 的样本的最有效方法

如何在msgraph.GraphServiceClient上进行身份验证?

Excel图表-使用openpyxl更改水平轴与Y轴相交的位置(Python)

如何让Flask 中的请求标签发挥作用

当独立的网络调用不应该互相阻塞时,'

avxspan与pandas period_range

在Python中动态计算范围

我如何根据前一个连续数字改变一串数字?

如何在表中添加重复的列?

Flash只从html表单中获取一个值

以逻辑方式获取自己的pyproject.toml依赖项

python中csv. Dictreader. fieldname的类型是什么?'

为什么'if x is None:pass'比'x is None'单独使用更快?