【问题标题】:QOpenGLWidget only updates when resizedQOpenGLWidget 仅在调整大小时更新
【发布时间】:2017-08-11 23:33:37
【问题描述】:

我得到了一个 opengl 示例,演示了如何使用纹理来显示 2d 图像。我已扩展示例以在QOpenGLWidget 中呈现。现在我想使用QTimer 来创建动画。

下面的代码应该在每次调用paintGL() 时将随机生成的 16x16 灰度图像绘制到纹理上。 QTimer 每 250 毫秒调用一次 update(),但我只在调整窗口大小时才看到场景更新,我不知道为什么。

我对 opengl 的经验有限,并不完全理解绘图代码所做的一切。 resizeGL() 中有什么东西也需要在 paintGL() 中吗?我是否误解了 QOpenGLWidget 应该如何工作?我应该使用提供的QOpenGLFunctions 而不是PyOpenGL

import numpy as np
from PyQt5 import QtCore, QtWidgets
from OpenGL.GL import *
from OpenGL.GL import shaders
from OpenGL.arrays import vbo


class GLWidget(QtWidgets.QOpenGLWidget):

    VERTEX_SHADER = """
        #extension GL_ARB_explicit_attrib_location : enable
        attribute vec4 in_position;
        attribute vec2 in_tex_coord;
        varying vec2 vs_tex_coord;

        void main(void){
            gl_Position = in_position;
            vs_tex_coord = in_tex_coord;
        }"""

    FRAGMENT_SHADER = """
        uniform sampler2D tex;
        varying vec2 vs_tex_coord;

        void main(void){
            gl_FragColor = texture2D(tex, vs_tex_coord);
        }"""

    vbov = vbo.VBO(np.array([[-1.0, -1.0, 0.0, 1.0, 0.0, 0.0],
                             [1.0, -1.0, 0.0, 1.0, 1.0, 0.0],
                             [1.0, 1.0, 0.0, 1.0, 1.0, 1.0],
                             [-1.0, 1.0, 0.0, 1.0, 0.0, 1.0]], 'f'))

    def __init__(self, *args, **kwargs):
        super(GLWidget, self).__init__(*args, **kwargs)
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.update)

    def initializeGL(self):
        glClearColor(0, 0, 0, 0)

        vs = shaders.compileShader(self.VERTEX_SHADER, GL_VERTEX_SHADER)
        fs = shaders.compileShader(self.FRAGMENT_SHADER, GL_FRAGMENT_SHADER)
        self.shader = shaders.compileProgram(vs, fs)

        self.position = glGetAttribLocation(self.shader, 'in_position')
        self.tex_coord = glGetAttribLocation(self.shader, 'in_tex_coord')

        self.timer.start(250)

    def paintGL(self):
        print 'paintGL'
        w, h, = 16, 16
        img = np.uint8(np.random.rand(w, h)*255)

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        shaders.glUseProgram(self.shader)
        try:
            self.vbov.bind()
            try:
                tex = glGenTextures(1)
                glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
                glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
                glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
                glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
                glTexImage2D(GL_TEXTURE_2D, 0, 3, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, img)
                tex_uloc = glGetUniformLocation(self.shader, "tex")
                glUniform1i(tex_uloc, 1)
                glActiveTexture(GL_TEXTURE0)
                glBindTexture(GL_TEXTURE_2D, tex)

                glEnableVertexAttribArray(self.position)
                glEnableVertexAttribArray(self.tex_coord)
                stride = 6 * 4
                glVertexAttribPointer(self.position, 4, GL_FLOAT, False, stride, self.vbov)
                glVertexAttribPointer(self.tex_coord, 2, GL_FLOAT, False, stride, self.vbov + 16)
                glDrawArrays(GL_TRIANGLE_FAN, 0, 4)
            finally:
                self.vbov.unbind()
                glDisableVertexAttribArray(self.position)
                glDisableVertexAttribArray(self.tex_coord)
        finally:
            shaders.glUseProgram(0)

    def resizeGL(self, width, height):
        glViewport(0, 0, width, height)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        glOrtho(0.0, width, 0.0, height, -1.0, 1.0)
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()


if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    win = GLWidget()
    win.show()
    app.exec_()

【问题讨论】:

  • 您必须调用repaint() 才能触发另一个帧。这可以从几个不同的地方完成。常见的选项是计时器、paintGL() 的结尾或您更改场景的某个地方(例如更新纹理的地方)。
  • 我认为这不是问题所在。对update() 的调用已经触发paintEvent。官方Qt 示例似乎都调用update()。我没有看到对repaint() 的任何引用。我尝试更改计时器以触发repaint(),但问题仍然相同。
  • 对不起,从问题中错过了。 update() 也应该可以正常工作。您能否验证 paintGL() 是否以预期的频率(例如控制台输出)被调用?
  • 是的,当计时器触发时,一切都被调用了(注意 print 语句)。这就是为什么我很难理解发生了什么。这可能与使用着色器有关吗?我将paintGl 代码放入this 示例中,update() 调用停止工作
  • 经过一天的折腾,我终于弄明白了。我需要在paintGL() 末尾添加对glBindTexture(GL_TEXTURE_2D, 0) 的调用。现在它工作正常

标签: python opengl pyqt pyqt5 pyopengl


【解决方案1】:

试试:

id = self.startTimer(time_intervel) in __init__()

然后,实现 timerEvent,

def timerEvent(self, event):
    self.update()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-19
    • 2021-06-24
    • 2013-09-19
    • 2016-08-19
    • 1970-01-01
    相关资源
    最近更新 更多