【发布时间】: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