PyOpenGL中glGenVertexArrays函数在部分脚本调用失败求助
在编写PyOpenGL程序显示四边形时,调用glGenVertexArrays函数时出现以下错误:
OpenGL.error.NullFunctionError: Attempt to call an undefined function glGenVertexArrays
推测问题与初始化流程有关。
相关代码
loader.py(核心为create_vao函数)
from raw_model import RawModel from OpenGL.GL import * from OpenGL.GLUT import * class Loader: __vaos = [] __vbos = [] def load_to_vao(self, positions: list): vao_id = self.create_vao() self.store_data_in_attribute_list(0, positions) self.unbind_vao() return RawModel(vao_id, len(positions)//3) @classmethod def clean_up(cls): for vao in cls.__vaos: glDeleteVertexArrays(vao) for vbo in cls.__vbos: glDeleteBuffers(vbo) @classmethod def create_vao(cls): vao_id = glGenVertexArrays(1) cls.__vaos.append(vao_id) glBindVertexArray(vao_id) return vao_id @classmethod def store_data_in_attribute_list(cls, attribute_number: int, data: list): vbo_id = glGenBuffers() cls.__vbos.append(vbo_id) glBindBuffer(GL_ARRAY_BUFFER, vbo_id) glBufferData(GL_ARRAY_BUFFER, data, GL_STATIC_DRAW) glVertexAttribPointer(attribute_number, 3, GL_FLOAT, False, 0, 0) glBindBuffer(GL_ARRAY_BUFFER, 0) @staticmethod def unbind_vao(): glBindVertexArray(0)
display_manager.py(核心为create_display函数)
from OpenGL.GL import * from OpenGL.GLUT import * class DisplayManager: def __init__(self, x: int = 1920, y: int = 1080): if x is not None: self.__width = x if y is not None: self.__height = y def create_display(self, window_name): glutInit() glutInitDisplayMode(GLUT_RGBA) # initialize colors glutInitWindowSize(self.get_width(), self.get_height()) # set windows size glutInitWindowPosition(0, 0) # set window position glutCreateWindow(f"{window_name}") # create window (with a name) and set window attribute print(glGenVertexArrays(1)) input("This ran") glutDisplayFunc(self.update_display) glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS) # prevent program from stopping @staticmethod def update_display(): glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # Remove everything from screen (i.e. displays all white) glLoadIdentity() # Reset all graphic/shape's position glutSwapBuffers() # Important for double buffering def get_width(self): return self.__width def get_height(self): return self.__height @classmethod def set_window(cls, window): cls.__WIND = window
已尝试在loader.py中加入glutInit()但无效,无法明确该函数的有效调用时机。
解决方案
原因分析
glGenVertexArrays是OpenGL 3.0及以上版本的核心函数,默认情况下GLUT创建的上下文可能是OpenGL 2.x版本,不支持VAO相关函数,导致函数未定义。另外,OpenGL函数的有效性依赖于已创建并激活的OpenGL上下文,如果在上下文创建前调用glGenVertexArrays也会触发该错误。
修复步骤
指定OpenGL版本并创建核心上下文
修改DisplayManager.create_display方法,强制创建OpenGL 3.2及以上的核心上下文:def create_display(self, window_name): glutInit() # 强制指定OpenGL版本和核心上下文 glutInitContextVersion(3, 2) glutInitContextProfile(GLUT_CORE_PROFILE) glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH) # 补充双缓冲和深度缓冲 glutInitWindowSize(self.get_width(), self.get_height()) glutInitWindowPosition(0, 0) glutCreateWindow(f"{window_name}") # 验证上下文版本 print(f"OpenGL版本: {glGetString(GL_VERSION)}") glutDisplayFunc(self.update_display) glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS)确保VAO函数在上下文创建后调用
所有OpenGL相关操作(包括Loader中的VAO创建)必须在glutCreateWindow之后执行,不能在上下文未初始化的代码中提前调用。修复BufferData参数错误
原代码中glBufferData直接传入Python列表会报错,需要转换为numpy数组并指定数据类型:
先安装numpy:pip install numpy,再修改store_data_in_attribute_list方法:import numpy as np @classmethod def store_data_in_attribute_list(cls, attribute_number: int, data: list): vbo_id = glGenBuffers() cls.__vbos.append(vbo_id) glBindBuffer(GL_ARRAY_BUFFER, vbo_id) # 转换为float32类型的numpy数组 data_array = np.array(data, dtype=np.float32) glBufferData(GL_ARRAY_BUFFER, data_array, GL_STATIC_DRAW) glVertexAttribPointer(attribute_number, 3, GL_FLOAT, GL_FALSE, 0, None) glEnableVertexAttribArray(attribute_number) # 必须启用顶点属性数组 glBindBuffer(GL_ARRAY_BUFFER, 0)启动GLUT主渲染循环
在创建窗口后需要启动主循环,否则程序会直接退出,添加调用代码:if __name__ == "__main__": dm = DisplayManager() dm.create_display("Quad Display") glutMainLoop()
内容的提问来源于stack exchange,提问作者Andreas Sabelfeld

