2015-11-03 83 views
17

我想使用Qt和PyOpenGL做一些實時繪圖並學習一些關於OpenGL的知識,但是即使讓我的初始測試數據出現,也遇到了麻煩。使用PyOpenGL繪圖時的麻煩

這個想法是將x座標和y座標存儲在不同的緩衝區中,因爲x座標幾乎不會改變,而y座標將幾乎改變每個渲染。不幸的是,讓他們進入不同的緩衝區會給我帶來問題。

現在我沒有錯誤,沒有任何顯示,所以我不知道該去哪裏。

這裏是我迄今爲止的繪圖類:

class GLWidget(QtOpenGL.QGLWidget): 
    def __init__(self, parent=None): 
     self.parent = parent 
     QtOpenGL.QGLWidget.__init__(self, parent) 
     self.current_position = 0 

    def initializeGL(self): 
     self.initGeometry() 

     self.vertex_code = """ 
      #version 120 
      attribute vec4 color; 
      attribute float x_position; 
      attribute float y_position; 
      varying vec4 v_color; 
      void main() 
      { 
       gl_Position = vec4(x_position, y_position, 0.0, 1.0); 
       v_color = color; 
      } """ 

     self.fragment_code = """ 
      #version 120 
      varying vec4 v_color; 
      void main() 
      { 
       //gl_FragColor = v_color; 
       gl_FragColor = vec4(1,1,1,1); 
      } """ 

     ## Build and activate program 
     # Request program and shader slots from GPU 
     self.program = GL.glCreateProgram() 
     self.vertex = GL.glCreateShader(GL.GL_VERTEX_SHADER) 
     self.fragment = GL.glCreateShader(GL.GL_FRAGMENT_SHADER) 

     # Set shaders source 
     GL.glShaderSource(self.vertex, self.vertex_code) 
     GL.glShaderSource(self.fragment, self.fragment_code) 

     # Compile shaders 
     GL.glCompileShader(self.vertex) 
     GL.glCompileShader(self.fragment) 

     # Attach shader objects to the program 
     GL.glAttachShader(self.program, self.vertex) 
     GL.glAttachShader(self.program, self.fragment) 

     # Build program 
     GL.glLinkProgram(self.program) 

     # Get rid of shaders (not needed anymore) 
     GL.glDetachShader(self.program, self.vertex) 
     GL.glDetachShader(self.program, self.fragment) 

     # Make program the default program 
     GL.glUseProgram(self.program) 

     # Create array object 
     self.vao = GL.glGenVertexArrays(1) 
     GL.glBindVertexArray(self.vao) 

     # Request buffer slot from GPU 
     self.x_data_buffer = GL.glGenBuffers(1) 
     GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.x_data_buffer) 
     GL.glBufferData(GL.GL_ARRAY_BUFFER, ArrayDatatype.arrayByteCount(self.x), self.x, GL.GL_DYNAMIC_DRAW) 

     self.y_data_buffer = GL.glGenBuffers(1) 
     GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.y_data_buffer) 
     GL.glBufferData(GL.GL_ARRAY_BUFFER, ArrayDatatype.arrayByteCount(self.y), self.y, GL.GL_DYNAMIC_DRAW) 




     ## Bind attributes 
     #self.stride = self.x.strides[0] 
     #self.offset = ctypes.c_void_p(0) 
     self.loc = GL.glGetAttribLocation(self.program, "x_position".encode('utf-8')) 
     GL.glEnableVertexAttribArray(self.loc) 
     GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.x_data_buffer) 
     GL.glVertexAttribPointer(self.loc, 1, GL.GL_FLOAT, GL.GL_FALSE, 0, 0) 

     #self.stride = self.y.strides[0] 
     #self.offset = ctypes.c_void_p(0) 
     self.loc = GL.glGetAttribLocation(self.program, "y_position".encode('utf-8')) 
     GL.glEnableVertexAttribArray(self.loc) 
     GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.y_data_buffer) 
     GL.glVertexAttribPointer(self.loc, 1, GL.GL_FLOAT, GL.GL_FALSE, 0, 0) 


    def resizeGL(self, width, height): 
     if height == 0: height = 1 

     GL.glViewport(0, 0, width, height) 
     GL.glMatrixMode(GL.GL_PROJECTION) 
     GL.glLoadIdentity() 
     aspect = width/float(height) 

     GLU.gluPerspective(45.0, aspect, 1.0, 100.0) 
     GL.glMatrixMode(GL.GL_MODELVIEW) 

    def paintGL(self): 
     GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) 

     GL.glDrawArrays(GL.GL_LINE_STRIP, 0, self.bins) 


    def initGeometry(self): 
     self.bins = 1000 

     self.x = np.linspace(-0.5,0.5,self.bins) 
     self.y = np.array([np.sin(val*2*np.pi) for val in self.x]) 
     self.color = np.array([(1,1,1,1) for x in np.arange(self.bins)]) 


    def addPoint(self, point): 
     #print('ADD POINT') 
     self.y[self.current_position] = point 
     if self.current_position < self.bins-1: 
      self.current_position += 1 
     else: 
      self.current_position = 0 

     return True 

    def render(self): 
     #print('RENDER') 
     self.updateGL() 
     return True 

我也把程序的完整工作版本瀏覽:

import sys 
from PyQt5.QtWidgets import QDesktopWidget, QMainWindow, QWidget, QAction, qApp, QApplication, QHBoxLayout, QVBoxLayout, QPushButton 
from PyQt5.QtCore import QTimer 
from PyQt5.QtGui import QIcon 
from PyQt5 import QtOpenGL 

from OpenGL import GL 
from OpenGL import GLU 
from OpenGL.arrays.arraydatatype import ArrayDatatype 

import ctypes 
import numpy as np 
from threading import Timer, Thread, Event 

class OxySensor(QMainWindow): 
    def __init__(self): 
     super().__init__() 
     self.initUI() 
     self.initActions() 
     self.initMenuBar() 
     self.initRenderTimer() 
     self.start() 

    def initUI(self): 
     self.resize(800,600) 
     self.center() 
     self.setWindowTitle('OxySensor') 

     okButton = QPushButton("OK") 
     cancelButton = QPushButton("Cancel") 


     hbox = QHBoxLayout() 
     hbox.addStretch(1) 
     hbox.addWidget(okButton) 
     hbox.addWidget(cancelButton) 

     vbox = QVBoxLayout() 
     #vbox.addStretch(1) 
     self.gl_widget = GLWidget() 
     vbox.addWidget(self.gl_widget) 
     vbox.addLayout(hbox) 

     mainWidget = QWidget(self) 
     mainWidget.setLayout(vbox) 

     self.setCentralWidget(mainWidget) 


     self.show() 

    def initActions(self): 
     self.exitAction = QAction(QIcon('images/close20.png'), '&Exit', self) 
     self.exitAction.setShortcut('Ctrl+W') 
     self.exitAction.setStatusTip('Exit application') 
     self.exitAction.triggered.connect(self.onExit) 

    def initMenuBar(self): 
     menubar = self.menuBar() 
     fileMenu = menubar.addMenu('&File') 
     fileMenu.addAction(self.exitAction) 
     return True 

    def initRenderTimer(self): 
     self.timer = QTimer() 
     self.timer.timeout.connect(self.gl_widget.render) 
     self.timer.start(100) 
     return True 

    def start(self): 
     self.stop_flag = Event() 
     self.thread = SerialPort(self.onTimerExpired, self.stop_flag) 
     self.thread.start() 
     self.statusBar().showMessage('Ready') 

    def center(self): 
     qr = self.frameGeometry() 
     cp = QDesktopWidget().availableGeometry().center() 
     qr.moveCenter(cp) 
     self.move(qr.topLeft()) 
     return True 

    def onTimerExpired(self): 
     data = np.random.uniform(-1,1) 
     self.gl_widget.addPoint(data) 
     return True 

    def onExit(self): 
     self.close() 
     return None 

    def closeEvent(self,event): 
     self.stop_flag.set() 
     event.accept() 
     return None 

class GLWidget(QtOpenGL.QGLWidget): 
    def __init__(self, parent=None): 
     self.parent = parent 
     QtOpenGL.QGLWidget.__init__(self, parent) 
     self.yRotDeg = 0.0 
     self.current_position = 0 

    def initializeGL(self): 
     self.initGeometry() 

     self.vertex_code = """ 
      #version 120 
      attribute vec4 color; 
      attribute float x_position; 
      attribute float y_position; 
      varying vec4 v_color; 
      void main() 
      { 
       gl_Position = vec4(x_position, y_position, 0.0, 1.0); 
       v_color = color; 
      } """ 

     self.fragment_code = """ 
      #version 120 
      varying vec4 v_color; 
      void main() 
      { 
       //gl_FragColor = v_color; 
       gl_FragColor = vec4(1,1,1,1); 
      } """ 

     ## Build and activate program 
     # Request program and shader slots from GPU 
     self.program = GL.glCreateProgram() 
     self.vertex = GL.glCreateShader(GL.GL_VERTEX_SHADER) 
     self.fragment = GL.glCreateShader(GL.GL_FRAGMENT_SHADER) 

     # Set shaders source 
     GL.glShaderSource(self.vertex, self.vertex_code) 
     GL.glShaderSource(self.fragment, self.fragment_code) 

     # Compile shaders 
     GL.glCompileShader(self.vertex) 
     GL.glCompileShader(self.fragment) 

     # Attach shader objects to the program 
     GL.glAttachShader(self.program, self.vertex) 
     GL.glAttachShader(self.program, self.fragment) 

     # Build program 
     GL.glLinkProgram(self.program) 

     # Get rid of shaders (not needed anymore) 
     GL.glDetachShader(self.program, self.vertex) 
     GL.glDetachShader(self.program, self.fragment) 

     # Make program the default program 
     GL.glUseProgram(self.program) 

     # Create array object 
     self.vao = GL.glGenVertexArrays(1) 
     GL.glBindVertexArray(self.vao) 

     # Request buffer slot from GPU 
     self.x_data_buffer = GL.glGenBuffers(1) 
     GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.x_data_buffer) 
     GL.glBufferData(GL.GL_ARRAY_BUFFER, ArrayDatatype.arrayByteCount(self.x), self.x, GL.GL_DYNAMIC_DRAW) 

     self.y_data_buffer = GL.glGenBuffers(1) 
     GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.y_data_buffer) 
     GL.glBufferData(GL.GL_ARRAY_BUFFER, ArrayDatatype.arrayByteCount(self.y), self.y, GL.GL_DYNAMIC_DRAW) 




     ## Bind attributes 
     #self.stride = self.x.strides[0] 
     #self.offset = ctypes.c_void_p(0) 
     self.loc = GL.glGetAttribLocation(self.program, "x_position".encode('utf-8')) 
     GL.glEnableVertexAttribArray(self.loc) 
     GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.x_data_buffer) 
     GL.glVertexAttribPointer(self.loc, 1, GL.GL_FLOAT, GL.GL_FALSE, 0, 0) 

     #self.stride = self.y.strides[0] 
     #self.offset = ctypes.c_void_p(0) 
     self.loc = GL.glGetAttribLocation(self.program, "y_position".encode('utf-8')) 
     GL.glEnableVertexAttribArray(self.loc) 
     GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.y_data_buffer) 
     GL.glVertexAttribPointer(self.loc, 1, GL.GL_FLOAT, GL.GL_FALSE, 0, 0) 


    def resizeGL(self, width, height): 
     if height == 0: height = 1 

     GL.glViewport(0, 0, width, height) 
     GL.glMatrixMode(GL.GL_PROJECTION) 
     GL.glLoadIdentity() 
     aspect = width/float(height) 

     GLU.gluPerspective(45.0, aspect, 1.0, 100.0) 
     GL.glMatrixMode(GL.GL_MODELVIEW) 

    def paintGL(self): 
     GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) 

     GL.glDrawArrays(GL.GL_LINE_STRIP, 0, self.bins) 


    def initGeometry(self): 
     self.bins = 1000 

     self.x = np.linspace(-0.5,0.5,self.bins) 
     self.y = np.array([np.sin(val*2*np.pi) for val in self.x]) 
     self.color = np.array([(1,1,1,1) for x in np.arange(self.bins)]) 


    def addPoint(self, point): 
     #print('ADD POINT') 
     self.y[self.current_position] = point 
     if self.current_position < self.bins-1: 
      self.current_position += 1 
     else: 
      self.current_position = 0 

     return True 

    def render(self): 
     #print('RENDER') 
     self.updateGL() 
     return True 

class SerialPort(Thread): 
    def __init__(self, callback, event): 
     Thread.__init__(self) 
     self.callback = callback 
     self.stopped = event 
     return None 

    def SetInterval(self, time_in_seconds): 
     self.delay_period = time_in_seconds 
     return True 

    def run(self): 
     while not self.stopped.wait(0.1): 
      self.callback() 
     return True 

if __name__ == '__main__': 
    app = QApplication(sys.argv) 
    oxy_sensor = OxySensor() 
    sys.exit(app.exec_()) 
+0

'updateGL()'方法在哪裏?到目前爲止發佈的代碼從未實際更新VBO內容後初始創建它們。 – derhass

+0

'updateGL()'在'render()'方法中 – user2027202827

+0

嗯,我可以看到它是_called_那裏。也許我誤解了一些東西,這個'updateGL'是pthon OpenGL小部件的一些方法。如果是這樣,那麼我的評論甚至應用更多:然後你從未實際更新那些VBO(我認爲這是在'updateGL'中完成的)。 – derhass

回答

3

問題是最有可能的是通話'OpenGL.GL'功能:

GL.glVertexAttribPointer(self.loc, 1, GL.GL_FLOAT, GL.GL_FALSE, 0, 0) 

注意最後一個值爲0(不幸的是沒有trans在C++ API中,後來到(void *)0,而是到一些高地址)。您最有可能的意思是「偏移0」,而不是「0對象的地址」。 I. e。使用None,而不是(它轉化爲(void *)0):

GL.glVertexAttribPointer(self.loc, 1, GL.GL_FLOAT, GL.GL_FALSE, 0, None) 

是的,這有點違反直覺。我花了半天的時間才弄清楚,在我自己的代碼中,輸出是黑色的。

另外要注意的是,如果不是OpenGL.GL功能使用Qt的的人(你通過gl = self.context().versionFunctions()獲得),他們提供了一個略微不同的API有你實際上傳遞0當你的意思是「偏移0」。

+0

嘿,我一直沒有在這個網站上,但只是想感謝你那個briliant catch將'None'傳遞給'GL.glVertexAttribPointer()':)感謝分享。 – user2027202827

-1

你應該看看它是基於OpenGL功能的程序阿伏伽德羅
http://avogadro.cc/wiki/Main_Page

源代碼是免費的,它可能是有用的。

+1

一個潛在解決方案的鏈接總是受歡迎的,但請在鏈接上添加上下文,以便您的同行用戶可以瞭解它是什麼以及它爲什麼在那裏。如果目標網站無法訪問或永久離線,請始終引用重要鏈接中最相關的部分。 – Exaqt

+0

...特別是因爲鏈接現在被破壞! – uhoh