PyQt6调用glBufferSubData更新数组后paintGL无法正常刷新问题
问题描述
按下左右方向键让4个点绕原点旋转时,屏幕无法正确更新,渲染出现破碎。顶点数组已正确传入缓冲区,但找不到异常原因。
相关代码
GLWidget类代码
import Shader from Math_3d import Vector3f from OpenGL.GL import * from PyQt6.QtOpenGLWidgets import QOpenGLWidget from PyQt6.QtCore import Qt class GLWidget(QOpenGLWidget): def __init__(self): super().__init__() # Vertex Buffer Object # Create point vertex data self.v3f = Vector3f([[0.5, 0.5, 0.0], [-0.5, -0.5, 0.0], [0.5, -0.5, 0.0], [-0.5, 0.5, 0.0]]) self.setFocusPolicy(Qt.FocusPolicy.StrongFocus) def initializeGL(self): program = glCreateProgram() buffer = glGenBuffers(1) # Request program and shader slots from the GPU vertex = glCreateShader(GL_VERTEX_SHADER) fragment = glCreateShader(GL_FRAGMENT_SHADER) # Set shader sources glShaderSource(vertex, Shader.vertex_code) glShaderSource(fragment, Shader.fragment_code) # Compile shaders and check that they have compiled glCompileShader(vertex) glCompileShader(fragment) if not glGetShaderiv(vertex, GL_COMPILE_STATUS): report_shader = glGetShaderInfoLog(vertex) print(report_shader) raise RuntimeError("Vertex shader compilation error") if not glGetShaderiv(fragment, GL_COMPILE_STATUS): report_frag = glGetShaderInfoLog(fragment) print(report_frag) raise RuntimeError("Fragment shader compilation error") # Link shaders to program glAttachShader(program, vertex) glAttachShader(program, fragment) glLinkProgram(program) if not glGetProgramiv(program, GL_LINK_STATUS): print(glGetProgramInfoLog(program)) raise RuntimeError('Linking error') # Get rid of shaders glDetachShader(program, vertex) glDetachShader(program, fragment) # Make default program to run glUseProgram(program) # Make this buffer the default one glBindBuffer(GL_ARRAY_BUFFER, buffer) # Vertex Array Buffer vao = glGenVertexArrays(1) glBindVertexArray(vao) strides = int(self.v3f.buffdata.nbytes/self.v3f.buffdata.itemsize) loc = glGetAttribLocation(program, 'position') glEnableVertexAttribArray(loc) glVertexAttribPointer(loc, 3, GL_FLOAT, False, strides, None) glBufferData(GL_ARRAY_BUFFER, self.v3f.buffdata.nbytes, self.v3f.buffdata, GL_DYNAMIC_DRAW) def paintGL(self): glClear(GL_COLOR_BUFFER_BIT) glPointSize(20) glDrawArrays(GL_POINTS, 0, self.v3f.buffdata.itemsize) def keyPressEvent(self, e): if e.key() == Qt.Key.Key_Left: self.v3f.Rotate(-0.1) print("Left") elif e.key() == Qt.Key.Key_Right: self.v3f.Rotate(0.1) print("Right") elif e.key() == Qt.Key.Key_Escape or Qt.Key.Key_Q: exit() self.update()
Vector3f模块代码
from OpenGL.GL import * import numpy as np np.set_printoptions(precision=3, suppress=True) class Vector3f: def __init__(self, ndarray): self.data = np.array([ndarray], dtype=np.float32) # Convert n-dimension arrays to one long array self.buffdata = self.data.ravel() self.orig = self.data self.__rval = 0.0 self.__sval = 0.0 def Rotate(self, val): # Rotation Matrix self.__rval += val # Create a memory value and add to that value m1 = np.asmatrix(self.data) # Convert n-dimension array to matrix rot2 = np.matrix([[np.cos(self.__rval), np.sin(-self.__rval), 0.0], # Create rotation matrix [np.sin(self.__rval), np.cos(self.__rval), 0.0], [0.0, 0.0, 0.0]], dtype=np.float32) rotator = m1 * rot2 # Matrix multiplication self.buffdata = rotator.ravel() # Convert result to array to be used on GPU buffer glBufferSubData(GL_ARRAY_BUFFER, 0, self.buffdata.data.nbytes, self.buffdata.data)
问题修复
以下是导致渲染破碎的关键问题及修复方案:
1. 缓冲区未持久绑定
initializeGL中buffer是局部变量,方法执行后销毁,后续调用glBufferSubData无法确保操作目标是正确的顶点缓冲区。
- 修复:将
buffer保存为GLWidget的实例变量self.buffer,并在旋转时传入Vector3f的Rotate方法,操作前绑定该缓冲区。
2. 顶点数量参数逻辑错误
paintGL中glDrawArrays的第三个参数使用self.v3f.buffdata.itemsize(数组元素字节数),虽数值巧合等于顶点数,但逻辑完全错误。
- 修复:直接传入顶点总数4,或通过
self.v3f.data.shape[0]动态获取。
3. 旋转矩阵规范错误
原旋转矩阵第三行第三列为0.0,会强制z坐标清零,不符合2D旋转矩阵规范(z轴应保持不变)。
- 修复:将旋转矩阵第三行改为
[0.0, 0.0, 1.0]。
4. 旋转状态未累积更新
Rotate方法始终基于初始数据self.data计算旋转,导致每次旋转都是重置而非累积。
- 修复:旋转后更新
self.data为最新结果,确保下一次旋转基于当前状态。
修复后的代码
修复后的GLWidget类
import Shader from Math_3d import Vector3f from OpenGL.GL import * from PyQt6.QtOpenGLWidgets import QOpenGLWidget from PyQt6.QtCore import Qt class GLWidget(QOpenGLWidget): def __init__(self): super().__init__() self.v3f = Vector3f([[0.5, 0.5, 0.0], [-0.5, -0.5, 0.0], [0.5, -0.5, 0.0], [-0.5, 0.5, 0.0]]) self.setFocusPolicy(Qt.FocusPolicy.StrongFocus) self.buffer = None self.program = None def initializeGL(self): self.program = glCreateProgram() self.buffer = glGenBuffers(1) vertex = glCreateShader(GL_VERTEX_SHADER) fragment = glCreateShader(GL_FRAGMENT_SHADER) glShaderSource(vertex, Shader.vertex_code) glShaderSource(fragment, Shader.fragment_code) glCompileShader(vertex) glCompileShader(fragment) if not glGetShaderiv(vertex, GL_COMPILE_STATUS): print(glGetShaderInfoLog(vertex)) raise RuntimeError("Vertex shader compilation error") if not glGetShaderiv(fragment, GL_COMPILE_STATUS): print(glGetShaderInfoLog(fragment)) raise RuntimeError("Fragment shader compilation error") glAttachShader(self.program, vertex) glAttachShader(self.program, fragment) glLinkProgram(self.program) if not glGetProgramiv(self.program, GL_LINK_STATUS): print(glGetProgramInfoLog(self.program)) raise RuntimeError('Linking error') glDetachShader(self.program, vertex) glDetachShader(self.program, fragment) glUseProgram(self.program) glBindBuffer(GL_ARRAY_BUFFER, self.buffer) vao = glGenVertexArrays(1) glBindVertexArray(vao) stride = 3 * 4 # 3个float × 4字节/浮点数 loc = glGetAttribLocation(self.program, 'position') glEnableVertexAttribArray(loc) glVertexAttribPointer(loc, 3, GL_FLOAT, False, stride, None) glBufferData(GL_ARRAY_BUFFER, self.v3f.buffdata.nbytes, self.v3f.buffdata, GL_DYNAMIC_DRAW) def paintGL(self): glClear(GL_COLOR_BUFFER_BIT) glPointSize(20) glDrawArrays(GL_POINTS, 0, 4) # 传入正确的顶点数量 def keyPressEvent(self, e): if e.key() == Qt.Key.Key_Left: self.v3f.Rotate(-0.1, self.buffer) print("Left") elif e.key() == Qt.Key.Key_Right: self.v3f.Rotate(0.1, self.buffer) print("Right") elif e.key() in (Qt.Key.Key_Escape, Qt.Key.Key_Q): exit() self.update()
修复后的Vector3f类
from OpenGL.GL import * import numpy as np np.set_printoptions(precision=3, suppress=True) class Vector3f: def __init__(self, ndarray): self.data = np.array(ndarray, dtype=np.float32) # 移除外层多余[],形状变为(4,3) self.buffdata = self.data.ravel() self.orig = self.data.copy() # 保存原始数据副本 self.__rval = 0.0 self.__sval = 0.0 def Rotate(self, val, buffer): self.__rval += val m1 = np.asmatrix(self.data) # 修正旋转矩阵的z轴分量 rot2 = np.matrix([[np.cos(self.__rval), np.sin(-self.__rval), 0.0], [np.sin(self.__rval), np.cos(self.__rval), 0.0], [0.0, 0.0, 1.0]], dtype=np.float32) rotator = m1 * rot2 # 更新当前数据为旋转后的结果 self.data = np.array(rotator) self.buffdata = rotator.ravel() # 绑定正确缓冲区后更新数据 glBindBuffer(GL_ARRAY_BUFFER, buffer) glBufferSubData(GL_ARRAY_BUFFER, 0, self.buffdata.nbytes, self.buffdata)
内容的提问来源于stack exchange,提问作者Treslav
相关产品推荐
相关产品推荐

