I found this nice tutorial of drawing and rotating a cube with PyQt and modern OpenGL. My objective was to adapt the script for point clouds, by doing the following (see also code below):
- Load point cloud using Open3D and extract coordinates & colors as numpy arrays
- Create Vertex Buffer Objects (VBOs) from the arrays
- Change the drawing function to
gl.glDrawElements(gl.GL_POINTS, ...)
Unfortunately then the point cloud is very distorted and thin (see screenshot). It should actually be a room with chairs and walls.
Do you see if I made a mistake with the VBOs or drawing? Or is there a better way of loading a point cloud?
I tested the example with the old fixed pipeline (glBegin(GL_POINTS)
... glEnd()
) and there the point cloud is correctly drawn (but also the performance really bad!).
from PyQt5 import QtCore # core Qt functionality
from PyQt5 import QtGui # extends QtCore with GUI functionality
from PyQt5 import QtOpenGL # provides QGLWidget, a special OpenGL QWidget
from PyQt5 import QtWidgets
import OpenGL.GL as gl # python wrapping of OpenGL
from OpenGL import GLU # OpenGL Utility Library, extends OpenGL functionality
from OpenGL.arrays import vbo
import numpy as np
import open3d as o3d
import sys
# Loading the point cloud from file
def load_pointcloud():
pcd = o3d.io.read_point_cloud("../pointclouds/0004.ply")
print(pcd)
print("Pointcloud Center: " + str(pcd.get_center()))
points = np.asarray(pcd.points)
colors = np.asarray(pcd.colors)
return points, colors
#### here was only the GUI code (slider, ...) , which works fine! ####
class GLWidget(QtOpenGL.QGLWidget):
def __init__(self, parent=None):
self.parent = parent
QtOpenGL.QGLWidget.__init__(self, parent)
def initializeGL(self):
self.qglClearColor(QtGui.QColor(250, 250, 250)) # initialize the screen to blue
gl.glEnable(gl.GL_DEPTH_TEST) # enable depth testing
self.initGeometryPC()
self.rotX = 0.0
self.rotY = 0.0
self.rotZ = 0.0
def setRotX(self, val):
self.rotX = val
def setRotY(self, val):
self.rotY = val
def setRotZ(self, val):
self.rotZ = val
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) #GLU.gluPerspective(45.0, aspect, 1.0, 100.0)
gl.glOrtho(-2.0, 2.0, -2.0, 2.0, 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() # push the current matrix to the current stack
gl.glTranslate(0.0, 0.0, -5.0) # third, translate cube to specified depth
#gl.glScale(.5, .5, .5) # second, scale point cloud
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) # first, translate point cloud center to origin
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.glPointSize(2)
gl.glDrawElements(gl.GL_POINTS, len(self.pointsIdxArray), gl.GL_UNSIGNED_INT, self.pointsIdxArray)
gl.glDisableClientState(gl.GL_VERTEX_ARRAY)
gl.glDisableClientState(gl.GL_COLOR_ARRAY)
gl.glPopMatrix() # restore the previous modelview matrix
# Push geometric data to GPU
def initGeometryPC(self):
points, colors = load_pointcloud()
self.pointsVtxArray = points
self.vertVBO = vbo.VBO(np.reshape(self.pointsVtxArray, (1, -1)).astype(np.float32))
self.vertVBO.bind()
self.pointsClrArray = colors
self.colorVBO = vbo.VBO(np.reshape(self.pointsClrArray, (1, -1)).astype(np.float32))
self.colorVBO.bind()
self.pointsIdxArray = np.arange(len(points))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())