Cutted cube with PyQt5 - python

I'm working with python3.9-64, I need to create a 3d cube then I followed this tutorial,
I divided the program in more files, I use PyQt5 but the tutorial uses PyQt4, I changed the modules which contain the calsses(example: PyQt4 has Gui.QMainWindow, PyQt5 has QtWidgets.QMainWindow)
this are the file I have
core.py
cubeVtxList = [
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[1.0, 1.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
[1.0, 0.0, 1.0],
[1.0, 1.0, 1.0],
[0.0, 1.0, 1.0]]
cubeClrList = [
[1.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[1.0, 1.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
[1.0, 0.0, 1.0],
[1.0, 1.0, 1.0],
[0.0, 1.0, 1.0]]
cubeIdxList = [
0, 1, 2, 3,
3, 2, 6, 7,
1, 0, 4, 5,
2, 1, 5, 6,
0, 3, 7, 4,
7, 6, 5, 4 ]
GLWidget.py
from PyQt5.QtGui import QColor
from PyQt5.QtOpenGL import QGLWidget
import OpenGL.GL as gl
import OpenGL.GLU as glu
from OpenGL.arrays import vbo
import numpy as np
from core import *
class GLWidget(QGLWidget):
def __init__(self, parent=None):
self.parent = parent
QGLWidget.__init__(self)
def initializeGL(self):
self.qglClearColor(QColor(0, 0, 0))
gl.glEnable(gl.GL_DEPTH_TEST)
self.initGeometry()
self.rotX = 0.0
self.rotY = 0.0
self.rotZ = 0.0
def resizeGL(self, width, height):
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.glPushMatrix()
gl.glTranslate(0.0, 0.0, -50.0) # third, translate cube to specified depth
gl.glScale(20.0, 20.0, 20.0) # second, scale cube
gl.glRotate(self.rotX, 1.0, 0.0, 0.0)
gl.glRotate(self.rotY, 0.0, 1.0, 0.0)
gl.glRotate(self.rotZ, 0.0, 0.0, 1.0)
gl.glTranslate(-0.5, -0.5, -0.5)
gl.glEnableClientState(gl.GL_VERTEX_ARRAY)
gl.glEnableClientState(gl.GL_COLOR_ARRAY)
gl.glVertexPointer(3, gl.GL_FLOAT, 0, self.vertVbo)
gl.glColorPointer(3, gl.GL_FLOAT, 0, self.colorVbo)
gl.glDrawElements(gl.GL_QUADS, len(self.cubeIdxArray), gl.GL_UNSIGNED_INT, self.cubeIdxArray)
gl.glDisableClientState(gl.GL_VERTEX_ARRAY)
gl.glDisableClientState(gl.GL_COLOR_ARRAY)
gl.glPopMatrix()
def initGeometry(self):
self.cubeVtxArray = np.array(cubeVtxList)
self.vertVbo = vbo.VBO(np.reshape(self.cubeVtxArray, (1, -1)).astype(np.float32))
self.vertVbo.bind()
self.cubeClrArray = np.array(cubeClrList)
self.colorVbo = vbo.VBO(np.reshape(self.cubeClrArray, (1, -1)).astype(np.float32))
self.colorVbo.bind()
self.cubeIdxArray = np.array(cubeIdxList)
def setRotX(self, val):
self.rotX = val
def setRotY(self, val):
self.rotY = val
def setRotZ(self, val):
self.rotZ = val
MWindow.py
from PyQt5.QtWidgets import QMainWindow, QSlider, QVBoxLayout, QWidget
from PyQt5 import QtCore
from GLWidget import GLWidget
from PyQt5.QtCore import QTimer
class MWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.resize(500, 500)
self.setWindowTitle('titolo')
self.glWidget = GLWidget(self)
self.initGui()
self.timer = QTimer()
self.timer.setInterval(20)
self.timer.timeout.connect(self.glWidget.updateGL)
self.timer.start()
def initGui(self):
central_widget = QWidget()
gui_layout = QVBoxLayout()
central_widget.setLayout(gui_layout)
self.setCentralWidget(central_widget)
gui_layout.addWidget(self.glWidget)
sliderX = QSlider(QtCore.Qt.Horizontal)
sliderX.valueChanged.connect(lambda val: self.glWidget.setRotX(val))
sliderY = QSlider(QtCore.Qt.Horizontal)
sliderY.valueChanged.connect(lambda val: self.glWidget.setRotY(val))
sliderZ = QSlider(QtCore.Qt.Horizontal)
sliderZ.valueChanged.connect(lambda val: self.glWidget.setRotZ(val))
gui_layout.addWidget(sliderX)
gui_layout.addWidget(sliderY)
gui_layout.addWidget(sliderZ)
Main.py
from MWindow import MWindow
from PyQt5.QtWidgets import QApplication
import sys
def Main():
app = QApplication([])
mw = MWindow()
mw.show()
sys.exit(app.exec_())
if __name__ == '__main__':
Main()
The result is supposed to be a cube that I can move with the sliders, but I get a cutted cube like in the following picture

The default dtype is float. When creating and indexing a buffer, you must use an integral datatype that corresponds to the type identifier in the draw call. In your case the specified type is GL_UNSIGNED_INT. Therefore the datatype must be uint32:
self.cubeIdxArray = np.array(cubeIdxList)
self.cubeIdxArray = np.array(cubeIdxList, dtype=np.uint32)
The last parameter of glVertexPointer si not the vertex buffer offset. It is the offset into the buffer objects data store. You have to bind the proper buffer specifying the attribute:
gl.glVertexPointer(3, gl.GL_FLOAT, 0, self.vertVbo)
gl.glColorPointer(3, gl.GL_FLOAT, 0, self.colorVbo)
self.vertVbo.bind()
gl.glVertexPointer(3, gl.GL_FLOAT, 0, None)
self.colorVbo.bind()
gl.glColorPointer(3, gl.GL_FLOAT, 0, None)

Just change the first entry in cubeClrList in your core.py from
cubeClrList = [
[1.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
to
cubeClrList = [
# v change this value
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],

Related

Why is the second QWidget in a layout disappearing? [duplicate]

I am trying to use PyQt5 to show two widgets, the first one is a plot of sin, cos and tan function. I am using the pyqtgraph and used the code that was found in the answer of this question. I am also using another widget that draws a cube using PyOpenGL, by taking the example found in this link. I am trying to show this two widgets in one main widget, which is the main window. My approach is the following
Take a main widget.
In the main widget, use a QVBoxLayout()
In the QVBoxLayout, at two widgets mentioned above
But when I am running the code, only the plot that is using the pyqtgraph is shown but not the cube that is drawn using PyOpenGL. After a little bit debugging, I was able to find out that the height of the cube widget is setting to 0 by default. I am not sure why this is hapenning. I tried calling glWidget.resize(640,480). But it didn't work. I am new on working with PyQt and PyOpenGL. I think I am missing some details that will allow the height of the glWidget to be greater than 0, if my assumption is correct. Also I am not sure if this is actually possible to do. My current code is given below, it is a little bit messy.
import sys
from OpenGL.GL.images import asWrapper
from PyQt5.QtWidgets import QApplication, QGridLayout
from PyQt5 import QtWidgets
import pyqtgraph as pg
from OpenGL.GL import *
from OpenGL.GLU import *
from PyQt5 import QtGui
from PyQt5.QtOpenGL import *
from PyQt5 import QtCore, QtWidgets
import pyqtgraph as pg
import numpy as np
from PyQt5 import QtOpenGL
import OpenGL.GL as gl
from OpenGL import GLU
from OpenGL.arrays import vbo
class TimeLine(QtCore.QObject):
frameChanged = QtCore.pyqtSignal(int)
def __init__(self, interval=60, loopCount=1, parent=None):
super(TimeLine, self).__init__(parent)
self._startFrame = 0
self._endFrame = 0
self._loopCount = loopCount
self._timer = QtCore.QTimer(self, timeout=self.on_timeout)
self._counter = 0
self._loop_counter = 0
self.setInterval(interval)
def on_timeout(self):
if self._startFrame <= self._counter < self._endFrame:
self.frameChanged.emit(self._counter)
self._counter += 1
else:
self._counter = 0
self._loop_counter += 1
if self._loopCount > 0:
if self._loop_counter >= self.loopCount():
self._timer.stop()
def setLoopCount(self, loopCount):
self._loopCount = loopCount
def loopCount(self):
return self._loopCounts
interval = QtCore.pyqtProperty(int, fget=loopCount, fset=setLoopCount)
def setInterval(self, interval):
self._timer.setInterval(interval)
def interval(self):
return self._timer.interval()
interval = QtCore.pyqtProperty(int, fget=interval, fset=setInterval)
def setFrameRange(self, startFrame, endFrame):
self._startFrame = startFrame
self._endFrame = endFrame
#QtCore.pyqtSlot()
def start(self):
self._counter = 0
self._loop_counter = 0
self._timer.start()
class GLWidget(QtOpenGL.QGLWidget):
def __init__(self, parent = None):
self.parent = parent
QtOpenGL.QGLWidget.__init__(self, parent)
self.resizeGL(640,800)
def initializeGL(self):
self.qglClearColor(QtGui.QColor(0,0,255))
gl.glEnable(gl.GL_DEPTH_TEST)
self.initGeometry()
self.rotX = 0.0
self.rotY = 0.0
self.rotZ = 0.0
def resizeGL(self, width, height):
gl.glViewport(0, 0, width, height)
gl.glMatrixMode(gl.GL_PROJECTION)
gl.glLoadIdentity()
print(width, height)
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.glPushMatrix()
gl.glTranslate(0.0, 0.0, -50.0)
gl.glScale(20.0, 20.0, 20.0)
gl.glRotate(self.rotX, 1.0, 0.0, 0.0)
gl.glRotate(self.rotY, 0.0, 1.0, 0.0)
gl.glRotate(self.rotZ, 0.0, 0.0, 1.0)
gl.glTranslate(-0.5, -0.5, -0.5)
gl.glEnableClientState(gl.GL_VERTEX_ARRAY)
gl.glEnableClientState(gl.GL_COLOR_ARRAY)
gl.glVertexPointer(3, gl.GL_FLOAT, 0, self.vertVBO)
gl.glColorPointer(3, gl.GL_FLOAT, 0, self.colorVBO)
gl.glDrawElements(gl.GL_QUADS, len(self.cubeIdxArray), gl.GL_UNSIGNED_INT, self.cubeIdxArray)
gl.glDisableClientState(gl.GL_VERTEX_ARRAY)
gl.glDisableClientState(gl.GL_COLOR_ARRAY)
gl.glPopMatrix()
def initGeometry(self):
self.cubeVtxArray = np.array(
[[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[1.0, 1.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
[1.0, 0.0, 1.0],
[1.0, 1.0, 1.0],
[0.0, 1.0, 1.0]])
self.vertVBO = vbo.VBO(np.reshape(self.cubeVtxArray,
(1, -1)).astype(np.float32))
self.vertVBO.bind()
self.cubeClrArray = np.array(
[[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[1.0, 1.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
[1.0, 0.0, 1.0],
[1.0, 1.0, 1.0],
[0.0, 1.0, 1.0 ]])
self.colorVBO = vbo.VBO(np.reshape(self.cubeClrArray,
(1, -1)).astype(np.float32))
self.colorVBO.bind()
self.cubeIdxArray = np.array(
[0, 1, 2, 3,
3, 2, 6, 7,
1, 0, 4, 5,
2, 1, 5, 6,
0, 3, 7, 4,
7, 6, 5, 4 ])
def setRotX(self, val):
self.rotX = np.pi * val
def setRotY(self, val):
self.rotY = np.pi * val
def setRotZ(self, val):
self.rotZ = np.pi * val
class MainGui(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.resize(600,600)
self.cube = GLWidget(self)
self.setupUI()
def setupUI(self):
central_widget = QtWidgets.QWidget()
central_layout = QtWidgets.QVBoxLayout()
central_widget.setLayout(central_layout)
self.setCentralWidget(central_widget)
pg.setConfigOption('background',0.95)
pg.setConfigOptions(antialias=True)
self.plot = pg.PlotWidget()
self.plot.setAspectLocked(lock = True, ratio = 0.01)
#self.cube = GLWidget(self)
#self.cube.resize(200,200)
central_layout.addWidget(self.cube)
central_layout.addWidget(self.plot)
self._plots = [self.plot.plot([], [], pen=pg.mkPen(color=color, width=2)) for color in ('g', 'r', 'y')]
self._timeline = TimeLine(loopCount = 0, interval = 10)
self._timeline.setFrameRange(0,720)
self._timeline.frameChanged.connect(self.generate_data)
self._timeline.start()
def plot_data(self, data):
for plt, val in zip(self._plots, data):
plt.setData(range(len(val)),val)
#QtCore.pyqtSlot(int)
def generate_data(self, i):
ang = np.arange(i, i + 720)
cos_func = np.cos(np.radians(ang))
sin_func = np.sin(np.radians(ang))
tan_func = sin_func/cos_func
tan_func[(tan_func < -3) | (tan_func > 3)] = np.NaN
self.plot_data([sin_func, cos_func, tan_func])
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
gui = MainGui()
gui.show()
sys.exit(app.exec_())
It seems that QGLWidget (which, by the way, is deprecated, and QOpenGLWidget should be used instead) doesn't implement sizeHint(), so it returns an invalid size (QSize(-1, -1)), which means that the widget can be possibly resized to a 0 width and/or height.
Since the plot widget has an expanding size policy (and dynamically reimplements sizeHint()) the result is that the gl widget is completely hidden, having 0 height.
A possible solution is to add the widgets with a proper stretch argument to the layout.
If you want both widgets to have the same height, you can do the following:
central_layout.addWidget(self.cube, stretch=1)
central_layout.addWidget(self.plot, stretch=1)
Note that the stretch is ratio-based (only integers are considered), so, if you want the cube have half the height of the plot:
central_layout.addWidget(self.cube, stretch=1)
central_layout.addWidget(self.plot, stretch=2)
Alternatively, you can use setMinimumHeight() for the gl widget, but since the plot has an expanding policy (which normally takes precedence), that gl widget will always have that height. A better solution would be to set an expanding policy for the gl widget and implement QSizeHint, but remember that the plot widget has a dynamic size hint, so it will always take some amount of "size priority".
class GLWidget(QtOpenGL.QGLWidget):
def __init__(self, parent = None):
QtOpenGL.QGLWidget.__init__(self, parent)
self.setSizePolicy(
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
def sizeHint(self):
return QtCore.QSize(300, 150)
# ...
There should be no need to manually call resizeGL() in the __init__, and you should also always use the dynamic access parent() function to get the parent.

Changing OpenGL Vertex Buffer Object data via pyopengl OpenGL.arrays.vbo has no effect

I am stuck trying to change data in a VBO.
I setup a scene with 2 Triangle primitives using a VBO via the python OpenGL.arrays.vbo helper class. That worked.
Then I want to change the data (in the minimal example below just shift one vertex when a button is clicked) which I cannot bring to work. I'm not sure if I use the VBO incorrectly or if there is some triviality blocking the redraw on the PyQt5 side.
Below is the full minimal example, the important stuff takes play in the member functions initializeGL, paintGL, and shift.
Inside GLWidget.shift I tried different approaches following the docs and this answer without success. Any help is appreciated.
#!/usr/bin/env python
import ctypes
import sys
import numpy as np
import OpenGL.arrays.vbo as glvbo
from PyQt5.QtCore import QSize
from PyQt5.QtWidgets import (QApplication, QHBoxLayout, QOpenGLWidget,
QWidget, QPushButton)
import OpenGL.GL as gl
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.glWidget = GLWidget()
button = QPushButton('shift', self)
button.clicked.connect(self.glWidget.shift)
layout = QHBoxLayout()
layout.addWidget(self.glWidget)
layout.addWidget(button)
self.setLayout(layout)
class GLWidget(QOpenGLWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.object = None
def minimumSizeHint(self):
return QSize(400, 400)
def initializeGL(self):
gl.glClearColor(0., 0., 0., 0.)
# a red and a green triangle
self.vertices = np.array([
# <- x,y,z -----> <- r,g,b -->
-0.5, -0.2, 0.0, 1.0, 0.0, 0.0,
0.5, -0.5, 0.0, 1.0, 0.0, 0.0,
0.5, 0.5, 0.0, 1.0, 0.0, 0.0,
0.4, -0.2, 0.0, 0.0, 1.0, 0.0,
1.4, -0.5, 0.0, 0.0, 1.0, 0.0,
1.4, 0.5, 0.0, 0.0, 1.0, 0.0,
], 'f')
self.vbo = glvbo.VBO(self.vertices)
self.vbo.bind()
self.object = gl.glGenLists(1)
gl.glNewList(self.object, gl.GL_COMPILE)
gl.glEnableClientState(gl.GL_VERTEX_ARRAY)
gl.glEnableClientState(gl.GL_COLOR_ARRAY)
buffer_offset = ctypes.c_void_p
stride = (3+3)*self.vertices.itemsize
gl.glVertexPointer(3, gl.GL_FLOAT, stride, None)
gl.glColorPointer(3, gl.GL_FLOAT, stride, buffer_offset(12))
gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
gl.glDisableClientState(gl.GL_VERTEX_ARRAY)
gl.glDisableClientState(gl.GL_COLOR_ARRAY)
gl.glEndList()
gl.glShadeModel(gl.GL_FLAT)
def paintGL(self):
gl.glClear(
gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
gl.glLoadIdentity()
gl.glRotated(50.0, 0.0, 1.0, 0.0)
gl.glCallList(self.object)
def resizeGL(self, width, height):
side = min(width, height)
if side < 0:
return
gl.glViewport((width - side) // 2, (height - side) // 2, side,
side)
gl.glMatrixMode(gl.GL_PROJECTION)
gl.glLoadIdentity()
gl.glOrtho(-1., +1., -1., +1., -100.0, 100.0)
gl.glMatrixMode(gl.GL_MODELVIEW)
def shift(self):
# shift y-position of one vertex
self.vertices[1] += 10.3
assert self.vertices is self.vbo.data
# version 1
# self.vbo.implementation.glBufferSubData(self.vbo.target, 0, self.vbo.data)
# version 2
# self.vbo[:] = self.vertices[:]
# self.vbo.bind()
# self.vbo.copy_data()
# version 2b (use slice)
# self.vbo[1:2] = self.vertices[1:2]
# self.vbo.bind()
# self.vbo.copy_data()
# version 3
self.vbo.set_array(self.vertices)
self.vbo.bind()
self.vbo.copy_data()
self.update()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
The code runs on an Ubuntu 18.04 machine under python 3.6 with
Vendor: Intel Open Source Technology Center
Renderer: Mesa DRI Intel(R) HD Graphics 5500 (Broadwell GT2)
OpenGL Version: 3.0 Mesa 19.2.8
Shader Version: 1.30
Sisplay lists (glGenList) are deprecated. What you try to encode in the list is the Vertex Specification.
I recommend to use a Vertex Array Object instead.
Create the VAO, before specifying the array of generic vertex attribute data:
class GLWidget(QOpenGLWidget):
# [...]
def initializeGL(self):
# [...]
self.vbo = glvbo.VBO(self.vertices)
self.vbo.bind()
self.vao = gl.glGenVertexArrays(1)
gl.glBindVertexArray(self.vao)
gl.glEnableClientState(gl.GL_VERTEX_ARRAY)
gl.glEnableClientState(gl.GL_COLOR_ARRAY)
buffer_offset = ctypes.c_void_p
stride = (3+3)*self.vertices.itemsize
gl.glVertexPointer(3, gl.GL_FLOAT, stride, None)
gl.glColorPointer(3, gl.GL_FLOAT, stride, buffer_offset(12))
gl.glBindVertexArray(0)
When you want to draw the object, then is sufficient to bind the VAO:
class GLWidget(QOpenGLWidget):
# [...]
def paintGL(self):
gl.glClear(
gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
gl.glLoadIdentity()
gl.glRotated(50.0, 0.0, 1.0, 0.0)
gl.glBindVertexArray(self.vao)
gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
gl.glBindVertexArray(0)
Note, the display list does not work, because certain commands are not compiled into the display list but are executed immediately, including glVertexPointer and glColorPointer. See glNewList.

glReadPixels is showing error with PyQt5. with GLUT Window it is working properly

I am developing a software in which I have an OpenGL window. I am creating the GUI of the software using PyQt5 and for the opengGL Window I am using QOpengGLWidget and for object selection, I am using Stencil Buffer and reading STENCIL_INDEX as the following function on mousePressEvent(self, event):
id = glReadPixels(event.x(), self.height - event.y() - 1, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)
but this is not working when I am using with Qt5 GUI and showing following error:
Traceback (most recent call last):
File "/home/akv26/Desktop/internProject/Main/opengl.py", line 92, in mousePressEvent
val = glReadPixels(event.x(), self.height - event.y() - 1, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)
File "/home/akv26/.local/lib/python3.7/site-packages/OpenGL/GL/images.py", line 371, in glReadPixels
imageData
File "/home/akv26/.local/lib/python3.7/site-packages/OpenGL/platform/baseplatform.py", line 402, in __call__
return self( *args, **named )
File "/home/akv26/.local/lib/python3.7/site-packages/OpenGL/error.py", line 232, in glCheckError
baseOperation = baseOperation,
OpenGL.error.GLError: GLError(
err = 1282,
description = b'invalid operation',
baseOperation = glReadPixels,
cArguments = (
183,
228,
1,
1,
GL_DEPTH_COMPONENT,
GL_FLOAT,
array([[0.]], dtype=float32),
)
)
Aborted (core dumped)
and here is the code for my custom QOpenGLWidget:
import sys
from PyQt5.QtWidgets import QOpenGLWidget
from PyQt5.QtCore import QSize, Qt
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
from math import *
class MyGLWidget(QOpenGLWidget):
def initializeGL(self):
glutInit()
glClearColor(1.0, 0.5, 0.2, 0.5)
glEnable(GL_DEPTH_TEST)
glEnable(GL_LIGHTING)
glEnable(GL_COLOR_MATERIAL)
glEnable(GL_LIGHT0)
glEnable(GL_LIGHT1)
glEnable(GL_NORMALIZE)
glShadeModel(GL_SMOOTH)
def resizeGL(self, w, h):
self.height=h
if h==0:
h=1
ratio = w * 1.0 / h
# Use the Projection Matrix
glMatrixMode(GL_PROJECTION)
# Reset Matrix
glLoadIdentity()
# Set the viewport to be the entire window
glViewport(0, 0, w, h)
# Set the correct perspective.
gluPerspective(45.0, ratio, 0.1, 100.0)
# Get Back to the Modelview
glMatrixMode(GL_MODELVIEW)
def paintGL(self):
glClearStencil(0)
glClear(GL_STENCIL_BUFFER_BIT)
glEnable(GL_STENCIL_TEST)
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)
ambientColor = [0.2,0.2,0.2,1.0]
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientColor)
lightColor0 = [0.5,0.5,0.5,1.0]
lightPos0 = [0, 0, -10.0, 1.0]
glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor0)
glLightfv(GL_LIGHT0, GL_POSITION, lightPos0)
lightColor1 = [0.5, 0.2, 0.2, 1.0]
lightPos1 = [-1.0, 0.5, 0.5, 0.0]
glLightfv(GL_LIGHT1, GL_DIFFUSE, lightColor1)
glLightfv(GL_LIGHT1, GL_POSITION, lightPos1)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
gluLookAt(-5, 5, -5, 0, 0, 0, 0.0, 1.0, 0.0)
self.drawSome()
def drawSome(self):
glStencilFunc(GL_ALWAYS, 1, -1)
glBegin(GL_QUADS)
glColor3f(0.0, 0.5, 0.0)
glNormal3f(0,0,-1)
glVertex3f(-1,-1,0)
glVertex3f(-1,1,0)
glVertex3f(1,1,0)
glVertex3f(1,-1,0)
glEnd()
glTranslatef(0,0,0)
glColor3f(0.5,0,0)
glStencilFunc(GL_ALWAYS, 2, -1)
glutSolidCube(1.0)
def mousePressEvent(self, event):
id = glReadPixels(event.x(), self.height - event.y() - 1, 1, 1, GL_STENCIL_INDEX, GL_UNSIGNED_INT)
print(id)
and the same is working properly when I am using this with GLUT window as following:
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
from math import *
import numpy as np
window_width=0
window_height=0
def changeSize(w, h):
global window_height, window_width
window_width=w
window_height=h
# Prevent a divide by zero, when window is too short
# (you cant make a window of zero width).
if h==0:
h=1
ratio = w * 1.0 / h
# Use the Projection Matrix
glMatrixMode(GL_PROJECTION)
# Reset Matrix
glLoadIdentity()
# Set the viewport to be the entire window
glViewport(0, 0, w, h)
# Set the correct perspective.
gluPerspective(45.0, ratio, 0.1, 100.0)
# Get Back to the Modelview
glMatrixMode(GL_MODELVIEW)
def drawSome():
glStencilFunc(GL_ALWAYS, 1, -1)
glBegin(GL_QUADS)
glColor3f(0.0, 0.5, 0.0)
glNormal3f(0,0,-1)
glVertex3f(-1,-1,0)
glVertex3f(-1,1,0)
glVertex3f(1,1,0)
glVertex3f(1,-1,0)
glEnd()
glTranslatef(0,0,0)
glColor3f(0.5,0,0)
glStencilFunc(GL_ALWAYS, 2, -1)
glutSolidCube(1.0)
def renderScene():
glClearStencil(0)
glClear(GL_STENCIL_BUFFER_BIT)
glEnable(GL_STENCIL_TEST)
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)
ambientColor = [0.2,0.2,0.2,1.0]
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientColor)
lightColor0 = [0.5,0.5,0.5,1.0] #Color
lightPos0 = [0, 0, -10.0, 1.0]
glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor0)
glLightfv(GL_LIGHT0, GL_POSITION, lightPos0)
lightColor1 = [0.5, 0.2, 0.2, 1.0]
lightPos1 = [-1.0, 0.5, 0.5, 0.0]
glLightfv(GL_LIGHT1, GL_DIFFUSE, lightColor1)
glLightfv(GL_LIGHT1, GL_POSITION, lightPos1)
# Clear Color and Depth Buffers
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
# Reset transformations
glLoadIdentity()
# Set the camera
# gluLookAt(x, 1.0, z, x+lx, 1.0, z+lz, 0.0, 1.0, 0.0)
gluLookAt(-5, 5, -5, 0, 0, 0, 0.0, 1.0, 0.0)
drawSome()
glutSwapBuffers()
def mouse(button, state, x, y):
global window_height
id = glReadPixels(x, window_height - y - 1, 1, 1, GL_STENCIL_INDEX, GL_UNSIGNED_INT)
print(id[0][0])
if __name__ == '__main__':
glutInit()
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA)
# glutInitWindowPosition(100,100)
glutInitWindowSize(320,320)
glutCreateWindow("Lighthouse3D - GLUT Tutorial")
glClearColor(1.0, 0.5, 0.2, 0.5)
# register callbacks
glutDisplayFunc(renderScene)
glutReshapeFunc(changeSize)
glutMouseFunc(mouse)
# glutTimerFunc(0, Timer, 0)
# here are the new entries
glutIgnoreKeyRepeat(1)
# OpenGL init
glEnable(GL_DEPTH_TEST)
glEnable(GL_LIGHTING)
glEnable(GL_COLOR_MATERIAL)
glEnable(GL_LIGHT0)
glEnable(GL_LIGHT1)
glEnable(GL_NORMALIZE)
glShadeModel(GL_SMOOTH)
glutMainLoop()
How to resolve the problem with QT5 GUI ?
I suspect there's no current OpenGL context when you call glReadPixels. You're mousePressEvent should probably call QOpenGLWidget::makeCurrent...
def mousePressEvent(self, event):
# Make the OpenGL context associated with this `QOpenGLWidget` current. and dont forget self
self.makeCurrent()
id = glReadPixels(event.x(), self.height - event.y() - 1, 1, 1, GL_STENCIL_INDEX, GL_UNSIGNED_INT)
print(id)
self.doneCurrent()
glReadPixels requires a render context to be current on the thread. Qt does automatic context managment and it's well documented that outside of methods named …GL the context may not be current.
You must make the context current explicitly outside of those methods, for which the Qt OpenGL classes offer the methods makeCurrent and doneCurrent. Brace your call of glReadPixels with calls to these functions; make sure to check, that makeCurrent succeeds, as the context may already be current in a different thread.

How to access the modelview matrix in kivy?

I am writing a python application using Kivy for rendering some graphics. I would like to find out how to get access to the current modelview matrix within kivy, the same matrix as in the vertex shader.
Here is my example code (Python 2.7, Kivy 1.9):
#!/usr/bin/env python
from kivy.app import App
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.graphics import *
from kivy.graphics.transformation import Matrix
from kivy.resources import resource_find
from kivy.uix.widget import Widget
vertices = [0.0, 0.0, 0.0, 0.0, 0.0, 1.0,
2.0, 0.0, 0.0, 0.0, 0.0, 1.0,
0.0, 2.0, 0.0, 0.0, 0.0, 1.0,
2.0, 2.0, 0.0, 0.0, 0.0, 1.0]
indices = [0, 1, 2, 1, 2, 3]
vformat = [(b'v_pos', 3, 'float'), (b'v_normal', 3, 'float')]
class Renderer(Widget):
def __init__(self, **kwargs):
super(Renderer, self).__init__(**kwargs)
self.canvas = RenderContext(compute_normal_mat=True)
self.canvas.shader.source = resource_find('simple.glsl')
with self.canvas:
PushMatrix()
Translate(0, -1, -3)
self.rot = Rotate(1, 0, 1, 0)
UpdateNormalMatrix()
self.mesh = Mesh(vertices=vertices, indices=indices,
fmt=vformat, mode='triangles')
PopMatrix()
Clock.schedule_interval(self.update_glsl, 1 / 60.)
def update_glsl(self, delta):
asp = self.width / float(self.height)
proj = Matrix().view_clip(-asp, asp, -1, 1, 1, 100, 1)
self.canvas['projection_mat'] = proj
self.rot.angle += delta * 100
print self.canvas['modelview_mat']
class RendererApp(App):
def build(self):
return Renderer()
if __name__ == "__main__":
RendererApp().run()
and the shader file simple.glsl:
---VERTEX SHADER-----------------------------------
attribute vec3 v_pos;
attribute vec3 v_normal;
uniform mat4 modelview_mat;
uniform mat4 projection_mat;
varying vec4 normal_vec;
void main (void) {
normal_vec = vec4(v_normal, 0.0);
gl_Position = projection_mat * modelview_mat * vec4(v_pos, 1.0);
}
---FRAGMENT SHADER---------------------------------
uniform mat4 normal_mat;
varying vec4 normal_vec;
void main (void){
vec4 v_normal = normalize( normal_mat * normal_vec ) ;
float theta = clamp(abs(dot(v_normal, vec4(0.0, 0.0, 1.0, 1.0))), 0.0, 1.0);
gl_FragColor = vec4(theta, theta, theta, 1.0);
}
In the function update_glsl I need the current modelview matrix for some calculations. I tried self.canvas['modelview_mat'], but this give me always the identity matrix.

PyOpenGL - passing transformation matrix into shader [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 7 years ago.
Improve this question
I am having trouble passing projection and modelview matrices into the GLSL shader from my PyOpenGL code. My understanding is that OpenGL matrices are column major, but when I pass in projection and modelview matrices as shown, I don't see anything. I tried the transpose of the matrices, and it worked for the modelview matrix, but the projection matrix doesn't work either way. Here is the code:
import OpenGL
from OpenGL.GL import *
from OpenGL.GL.shaders import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from OpenGL.GLUT.freeglut import *
from OpenGL.arrays import vbo
import numpy, math, sys
strVS = """
attribute vec3 aVert;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform vec4 uColor;
varying vec4 vCol;
void main() {
// option #1 - fails
gl_Position = uPMatrix * uMVMatrix * vec4(aVert, 1.0);
// option #2 - works
gl_Position = vec4(aVert, 1.0);
// set color
vCol = vec4(uColor.rgb, 1.0);
}
"""
strFS = """
varying vec4 vCol;
void main() {
// use vertex color
gl_FragColor = vCol;
}
"""
# 3D scene
class Scene:
# initialization
def __init__(self):
# create shader
self.program = compileProgram(compileShader(strVS,
GL_VERTEX_SHADER),
compileShader(strFS,
GL_FRAGMENT_SHADER))
glUseProgram(self.program)
self.pMatrixUniform = glGetUniformLocation(self.program, 'uPMatrix')
self.mvMatrixUniform = glGetUniformLocation(self.program,
"uMVMatrix")
self.colorU = glGetUniformLocation(self.program, "uColor")
# attributes
self.vertIndex = glGetAttribLocation(self.program, "aVert")
# color
self.col0 = [1.0, 1.0, 0.0, 1.0]
# define quad vertices
s = 0.2
quadV = [
-s, s, 0.0,
-s, -s, 0.0,
s, s, 0.0,
s, s, 0.0,
-s, -s, 0.0,
s, -s, 0.0
]
# vertices
self.vertexBuffer = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer)
vertexData = numpy.array(quadV, numpy.float32)
glBufferData(GL_ARRAY_BUFFER, 4*len(vertexData), vertexData,
GL_STATIC_DRAW)
# render
def render(self, pMatrix, mvMatrix):
# use shader
glUseProgram(self.program)
# set proj matrix
glUniformMatrix4fv(self.pMatrixUniform, 1, GL_FALSE, pMatrix)
# set modelview matrix
glUniformMatrix4fv(self.mvMatrixUniform, 1, GL_FALSE, mvMatrix)
# set color
glUniform4fv(self.colorU, 1, self.col0)
#enable arrays
glEnableVertexAttribArray(self.vertIndex)
# set buffers
glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer)
glVertexAttribPointer(self.vertIndex, 3, GL_FLOAT, GL_FALSE, 0, None)
# draw
glDrawArrays(GL_TRIANGLES, 0, 6)
# disable arrays
glDisableVertexAttribArray(self.vertIndex)
class Renderer:
def __init__(self):
pass
def reshape(self, width, height):
self.width = width
self.height = height
self.aspect = width/float(height)
glViewport(0, 0, self.width, self.height)
glEnable(GL_DEPTH_TEST)
glDisable(GL_CULL_FACE)
glClearColor(0.8, 0.8, 0.8,1.0)
glutPostRedisplay()
def keyPressed(self, *args):
sys.exit()
def draw(self):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
# build projection matrix
fov = math.radians(45.0)
f = 1.0/math.tan(fov/2.0)
zN, zF = (0.1, 100.0)
a = self.aspect
pMatrix = numpy.array([f/a, 0.0, 0.0, 0.0,
0.0, f, 0.0, 0.0,
0.0, 0.0, (zF+zN)/(zN-zF), -1.0,
0.0, 0.0, 2.0*zF*zN/(zN-zF), 0.0], numpy.float32)
# modelview matrix
mvMatrix = numpy.array([1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.5, 0.0, -5.0, 1.0], numpy.float32)
# render
self.scene.render(pMatrix, mvMatrix)
# swap buffers
glutSwapBuffers()
def run(self):
glutInitDisplayMode(GLUT_RGBA)
glutInitWindowSize(400, 400)
self.window = glutCreateWindow("Minimal")
glutReshapeFunc(self.reshape)
glutDisplayFunc(self.draw)
glutKeyboardFunc(self.keyPressed) # Checks for key strokes
self.scene = Scene()
glutMainLoop()
glutInit(sys.argv)
prog = Renderer()
prog.run()
When I use option #2 in the shader without either matrix, I get the following output:
What am I doing wrong?

Categories