使用ManimGL制作带视频纹理的波浪动画时触发OpenGL无效枚举错误
ManimGL 长时长动画抛出 Invalid Enum 错误(OpenGL GLError 1280)
问题描述
使用 ManimGL 制作带视频帧纹理的波浪动画时,短时长(约5秒)动画运行正常,但时长超过10秒会抛出 invalid enum error,动画时长由 PinkyWaving 类的 self.wait 方法控制,目标是生成2分钟完整动画。
错误信息
self.construct() File "pinky_waving.py", line 119, in construct self.wait(10) File "f:\instalaciones\anaconda\envs\manim2\lib\site-packages\manimlib\scene\scene.py", line 417, in wrapper func(self, *args, **kwargs) File "f:\instalaciones\anaconda\envs\manim2\lib\site-packages\manimlib\scene\scene.py", line 508, in wait self.emit_frame() File "f:\instalaciones\anaconda\envs\manim2\lib\site-packages\manimlib\scene\scene.py", line 179, in emit_frame self.file_writer.write_frame(self.camera) File "f:\instalaciones\anaconda\envs\manim2\lib\site-packages\manimlib\scene\scene_file_writer.py", line 238, in write_frame raw_bytes = camera.get_raw_fbo_data() File "f:\instalaciones\anaconda\envs\manim2\lib\site-packages\manimlib\camera\camera.py", line 294, in get_raw_fbo_data gl.glBindFramebuffer(gl.GL_READ_FRAMEBUFFER, self.fbo_msaa.glo) File "src/errorchecker.pyx", line 58, in OpenGL_accelerate.errorchecker._ErrorChecker.glCheckError OpenGL.error.GLError: GLError( err = 1280, description = b'numeraci\xf3n no v\xe1lida', baseOperation = glBindFramebuffer, cArguments = (GL_READ_FRAMEBUFFER, 2) )
环境配置
- Python 3.8
- ManimGL v1.5.0
- moderngl 5.7.2
原始实现代码
from manimlib import * import os VIDEO_FRAMES = 'pinky_60fps-opencv' TEXTURES = os.listdir(VIDEO_FRAMES) N = len(TEXTURES) FRAME_INDEX = 0 class TexturedPlaneWavingVideo(TexturedSurface): CONFIG = { 'waving_plane_config': { "u_range": (-5*2,5*2), "v_range": (-5*2,5*2), "resolution": (32,32), "color": BLUE_E, "opacity": 0.85, } } def __init__(self, phi=0, height_factor=1.0, d=1.0, alpha=0.1,scale_factor = 0.5, init_frame= None,**kwargs): """ init_frame: str frame used as texture """ digest_config(self,self.CONFIG) self.init_frame = init_frame # Parameters # heigth of wave self.height_factor = height_factor self.d = d self.alpha = alpha self.phi = phi #overall scale of plane self.scale_factor = scale_factor # Initialize as a plane surface = ParametricSurface( uv_func = self.wave_func, **self.waving_plane_config).scale(self.scale_factor) self.surface = surface super().__init__(uv_surface= surface, image_file = os.path.join(VIDEO_FRAMES,self.init_frame)) def update_wave(self,dt): self.phi += 4*dt global FRAME_INDEX FRAME_INDEX= (FRAME_INDEX+1)%N texture = TEXTURES[FRAME_INDEX] texture_path =os.path.join(VIDEO_FRAMES, TEXTURES[FRAME_INDEX]) # Create a new TexturedPlaneWavingVideo plane = TexturedPlaneWavingVideo( phi=self.phi, height_factor=self.height_factor, d=self.d ,alpha=self.alpha,scale_factor = self.scale_factor, init_frame=texture ) self.init_frame = texture self.texture_paths = { "LightTexture": get_full_raster_image_path(texture_path), "DarkTexture": get_full_raster_image_path(texture_path),} self.become(plane) self.init_shader_data() def plane_func(self,u,v): return np.array([ u, v, 0]) def wave_func(self, u, v): return (u, v, np.sin((np.sqrt(u**2/self.d + v ** 2 / self.d) + self.phi)) * self.height_factor + self.alpha) def wave_func_np(self,u,v): return np.array( [u, v, np.sin( (np.sqrt(u**2/self.d + v ** 2 / self.d ) + self.phi) ) * self.height_factor + self.alpha]) def waving(self,speed = 1): """ speed: float speed of waving animation """ self.add_updater(lambda m,dt:self.update_wave(dt*speed) ) def stop_waving(self): self.clear_updaters() class PinkyWaving(ThreeDScene): def construct(self): p = TexturedPlaneWavingVideo(phi=0, height_factor=3.0, d=1.0, alpha=0.1,scale_factor = 0.5,init_frame="frame0-00-00.00.jpg") self.play(ShowCreation(p)) p.waving() self.wait(10)
问题根源与解决方案
问题分析
错误的核心原因是每次更新时创建新的TexturedPlaneWavingVideo实例,导致大量OpenGL资源(帧缓冲、纹理对象)无法被正确回收,长时间运行后资源耗尽,触发GL_INVALID_ENUM错误(操作了已失效的GL对象)。
修改后的代码
from manimlib import * import os VIDEO_FRAMES = 'pinky_60fps-opencv' TEXTURES = sorted(os.listdir(VIDEO_FRAMES)) # 确保帧顺序正确 N = len(TEXTURES) class TexturedPlaneWavingVideo(TexturedSurface): CONFIG = { 'waving_plane_config': { "u_range": (-5*2, 5*2), "v_range": (-5*2, 5*2), "resolution": (32, 32), "color": BLUE_E, "opacity": 0.85, } } def __init__(self, phi=0, height_factor=1.0, d=1.0, alpha=0.1, scale_factor=0.5, init_frame=None, **kwargs): digest_config(self, self.CONFIG) self.init_frame = init_frame self.height_factor = height_factor self.d = d self.alpha = alpha self.phi = phi self.scale_factor = scale_factor self.frame_index = 0 # 用实例变量替代全局变量 # 初始化曲面 surface = ParametricSurface( uv_func=self.wave_func, **self.waving_plane_config ).scale(self.scale_factor) self.surface = surface super().__init__( uv_surface=surface, image_file=os.path.join(VIDEO_FRAMES, self.init_frame) ) def update_wave(self, dt): # 更新波浪相位 self.phi += 4 * dt # 更新帧索引 self.frame_index = (self.frame_index + 1) % N current_frame = TEXTURES[self.frame_index] texture_path = os.path.join(VIDEO_FRAMES, current_frame) # 更新纹理 self.texture_paths = { "LightTexture": get_full_raster_image_path(texture_path), "DarkTexture": get_full_raster_image_path(texture_path), } self.init_shader_data() # 更新曲面形状(无需重建曲面,直接刷新参数) self.surface.set_uv_func(self.wave_func) self.surface.generate_points() self.surface.make_smooth() def plane_func(self, u, v): return np.array([u, v, 0]) def wave_func(self, u, v): return ( u, v, np.sin((np.sqrt(u**2/self.d + v ** 2 / self.d) + self.phi)) * self.height_factor + self.alpha ) def wave_func_np(self, u, v): return np.array( [ u, v, np.sin((np.sqrt(u**2/self.d + v ** 2 / self.d) + self.phi)) * self.height_factor + self.alpha ] ) def waving(self, speed=1): self.add_updater(lambda m, dt: self.update_wave(dt*speed)) def stop_waving(self): self.clear_updaters() class PinkyWaving(ThreeDScene): def construct(self): p = TexturedPlaneWavingVideo( phi=0, height_factor=3.0, d=1.0, alpha=0.1, scale_factor=0.5, init_frame="frame0-00-00.00.jpg" ) self.play(ShowCreation(p)) p.waving() self.wait(120) # 直接设置2分钟时长
关键修改点
- 移除全局变量:用实例变量
self.frame_index替代全局FRAME_INDEX,避免多实例状态冲突 - 避免重复创建实例:删除每次更新时创建新
TexturedPlaneWavingVideo的逻辑,减少OpenGL资源开销 - 直接更新现有对象:
- 仅更新纹理路径并重新初始化着色器数据
- 调用
set_uv_func+generate_points刷新曲面形状,无需重建曲面
- 确保帧顺序:对
TEXTURES列表排序,保证视频帧播放顺序正确
额外优化建议
- 预加载纹理:将所有视频帧提前加载到内存,避免磁盘IO拖慢动画速度
- 禁用MSAA:如果对画质要求不高,可在场景中添加
CONFIG = {"camera_config": {"use_msaa": False}},减少帧缓冲资源消耗
运行命令保持不变:manimgl pinky.py PinkyWaving -w
内容的提问来源于stack exchange,提问作者Hōgyoku
相关产品推荐
相关产品推荐

