This is my first question on th stackoverflow so sorry for any mistakes, but I don't now how to deal with my problem. I am writing an application that renders a point cloud using OpenGl in pyQt5. I am able to render all the points using VBO, but the only way to see points is by normalizing its coordinates to -0.5 - 0.5 values. Could you help me dealing with that?
This is my code:
def initializeGL(self):
self.setClearColor(self.backgroundColor)
self.object = self.makeDefaultObject()
if not self.drawDefaultData and self.pts.size == 0: self.pts = default.pointCloud
gl.glShadeModel(gl.GL_FLAT)
gl.glEnable(gl.GL_DEPTH_TEST)
gl.glEnable(gl.GL_CULL_FACE)
self.reloadPoints()
def reloadPoints(self):
if self.pts.size == 0:
self.pts = default.pointCloud
self.vbo_disp, self.vbo_disp_clr, self.disp_count = self.loadVBO()
self.xPos = -np.mean(self.pts, axis=0)[0]
self.yPos = np.mean(self.pts, axis=0)[1]
self.zPos = np.min(self.pts, axis=0)[2] -10
def paintGL(self):
gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
gl.glLoadIdentity()
gl.glTranslated(self.xPos, self.yPos, self.zPos)
gl.glScaled(self.zoomScale, self.zoomScale, self.zoomScale)
gl.glRotated(self.xRot / 16.0, 1.0, 0.0, 0.0)
gl.glRotated(self.yRot / 16.0, 0.0, 1.0, 0.0)
gl.glRotated(self.zRot / 16.0, 0.0, 0.0, 1.0)
gl.glCallList(self.object)
if self.pts.size != 0: self.drawPointCloud()
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(-0.5, +0.5, +0.5, -0.5, 4.0, 15.0)
gl.glMatrixMode(gl.GL_MODELVIEW)
def drawPointCloud(self):
gl.glPushMatrix()
gl.glPointSize(self.pointSize)
glEnableClientState(gl.GL_VERTEX_ARRAY)
glEnableClientState(gl.GL_COLOR_ARRAY)
vtx_disp = self.vbo_disp[0]
clr_disp = self.vbo_disp_clr[0]
cnt_disp = self.disp_count[0]
vtx_disp.bind()
gl.glVertexPointer(3, gl.GL_FLOAT, 0, vtx_disp)
vtx_disp.unbind()
clr_disp.bind()
gl.glColorPointer(3, gl.GL_FLOAT, 0, clr_disp)
clr_disp.unbind()
gl.glDrawArrays(gl.GL_POINTS, 0, cnt_disp)
glDisableClientState(gl.GL_VERTEX_ARRAY)
glDisableClientState(gl.GL_COLOR_ARRAY)
gl.glPopMatrix()
def loadVBO(self):
vtx_list = [ [] for _ in range(1) ]
clr_list = [ [] for _ in range(1) ]
vtx_count = np.zeros( 1, dtype=np.int32 )
vtx_count[0] = len(self.pts)
vtx_list[0] = qlVBO.VBO( self.pts[:,:3].copy().astype(np.float32) )
if (np.size(self.pts, 1) == 6):
clr_list[0] = qlVBO.VBO( self.pts[:,3:].copy().astype(np.float32) / 255.0 )
elif (np.size(self.pts, 1) == 3):
clr_list[0] = qlVBO.VBO( np.ones([vtx_count[0],3]).astype(np.float32) )
else:
print("Internal error")
vtx_count[0] = len(self.pts)
return vtx_list, clr_list, vtx_count
I have been trying to change the way that camera is positioned but without any results. In my opinion the problem is in:
gl.glOrtho(-0.5, +0.5, +0.5, -0.5, 4.0, 15.0)
But how to change that... Please help me with that!
What you can see on the viewport, is the projection of the 3 dimensional viewing volume.
The projection matrix defines the area (volume) with respect to the observer (viewer) which is projected onto the viewport.
In your case you use an Orthographic projection. At orthographic projection, this area (volume) is defined by 6 distances (left, right, bottom, top, near and far) to the viewer's position. All the objects (points) which are in the space (volume) are "visible" on the viewport. All the objects (points) which are out of this space are clipped at the borders of the volume.
[...] but the only way to see points is by normalizing its coordinates to -0.5 - 0.5 values [...]
Actually you do it the wrong way. You change the scale of the vertex coordinates (points), rather than expanding the viewing volume.
The orthographic projection can be set by glOrtho
:
gl.glOrtho(-0.5, +0.5, +0.5, -0.5, 4.0, 15.0)
This setup defines a cuboid volume with the left, bottom, near od (-0.5, -0.5, 4.0) and the right, top, far of (0.5, 0.5, 15.0).
Increase that volume, rather than scaling the coordinates:
gl.glOrtho(min_x, max_x, max_y, min_y, min_z, max_z)
I recommend to do the following:
Compute the axis aligned bounding box of the point cloud, from (self.min_x
, self._min_y
, self.min_z
) to (self.max_x
, self._max_y
, self.max_z
) and define an orthgrphic projection, which defines a cuboid volume, that large enough to enclose all the points independent on its orientation (Euclidean distance):
ef resizeGL(self, width, height):
side = min(width, height)
if side < 0:
return
gl.glViewport((width - side) // 2, (height - side) // 2, side, side)
dx = self.max_x - self.min_x
dy = self.max_y - self.min_y
dz = self.max_z - self.min_z
dia = math.sqrt(dx*dx + dy*dy + dz*dz)
gl.glMatrixMode(gl.GL_PROJECTION)
gl.glLoadIdentity()
gl.glOrtho(-dia/2, dia/2, dia/2, -dia/2, -dia/2, dia/2)
gl.glMatrixMode(gl.GL_MODELVIEW)
Compute the center of the bounding box and define an initial translation in the opposite direction. The translation has to "move" the center of the point cloud to the origin of the world:
self.center_x = (self.min_x + self.max_x) / 2
self.center_y = (self.min_y + self.max_y) / 2
self.center_z = (self.min_z + self.max_z) / 2
self.xPos = 0
self.yPos = 0
self.zPos = 0
The viewing volume is large enough, so there is no necessity to scale the points (self.zoomScale = 1
). It is important to scale the point cloud first, then translate it and final rotate it, thus the pivot of the rotation is the center of the point cloud:
modelview = translate * rotation * scale * translateToOrigin
The matrix transformation operations like glRotate
, glScale
and glTranslate
, define a new matrix and multiply the current matrix by the new matrix. Hence the order of the operations has to be 1. glTranslate
2. glRotate
, 3. glScale
, 4. glTranslate(-center)
, :
def paintGL(self):
gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
gl.glLoadIdentity()
gl.glTranslated(self.xPos, self.yPos, self.zPos)
gl.glRotated(self.zRot / 16.0, 0.0, 0.0, 1.0)
gl.glRotated(self.yRot / 16.0, 0.0, 1.0, 0.0)
gl.glRotated(self.xRot / 16.0, 1.0, 0.0, 0.0)
gl.glScaled(self.zoomScale, self.zoomScale, self.zoomScale)
gl.glTranslated(-self.center_x, -self.center_y, -self.center_z)
# [...]