pyQt5 中的 pyOpenGL - 点云渲染中的相机位置

pyOpenGL in pyQt5 - camera position in point cloud rendering

这是我在 Whosebug 上的第一个问题,对于任何错误,我深表歉意,但我现在不知道如何处理我的问题。我正在编写一个在 pyQt5 中使用 OpenGl 渲染点云的应用程序。我可以使用 VBO 渲染所有点,但查看点的唯一方法是将其坐标标准化为 -0.5 - 0.5 值。你能帮我处理一下吗?

这是我的代码:

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

我一直在尝试改变相机的定位方式,但没有任何结果。我认为问题出在:

gl.glOrtho(-0.5, +0.5, +0.5, -0.5, 4.0, 15.0)

但是如何改变...请帮助我!

您在视口上看到的是 3 维观看体积的投影。 投影矩阵定义了投影到视口上的观察者(观察者)的面积(体积)。
在您的情况下,您使用 Orthographic projection。在正交投影中,该区域(体积)由到观察者位置的 6 个距离(左、右、下、上、近和远)定义。 space(体积)中的所有对象(点)在视口上都是 "visible"。 space 之外的所有对象(点)都在体积的边界处被剪裁。

[...] but the only way to see points is by normalizing its coordinates to -0.5 - 0.5 values [...]

其实你做错了。您更改顶点坐标(点)的比例,而不是扩大查看体积。
正射投影可以通过glOrtho:

设置
gl.glOrtho(-0.5, +0.5, +0.5, -0.5, 4.0, 15.0)

此设置定义了一个长方体体积,左、下、近 od (-0.5, -0.5, 4.0) 和右、上、远 (0.5, 0.5, 15.0)。
增加体积,而不是缩放坐标:

gl.glOrtho(min_x, max_x, max_y, min_y, min_z, max_z)

我建议执行以下操作:

计算点云的轴对齐边界框,从(self.min_x, self._min_y, self.min_z)到(self.max_x, self._max_y, self.max_z) 并定义一个正交投影,它定义了一个长方体体积,该体积足够大以包含与其方向无关的所有点 (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)

计算边界框的中心并定义相反方向的初始平移。翻译必须 "move" 点云的中心到世界的原点:

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

观看量足够大,没必要缩放点(self.zoomScale = 1)。重要的是先缩放点云,然后平移,最后旋转,这样旋转的轴心就是点云的中心:

modelview = translate * rotation * scale * translateToOrigin 

类似glRotate, glScale and glTranslate的矩阵变换操作,定义一个新矩阵,将当前矩阵乘以新矩阵。因此操作的顺序必须是 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)

    # [...]