【问题标题】:Linux OpenGL code fails, works on Mac & WindowsLinux OpenGL 代码失败,适用于 Mac 和 Windows
【发布时间】:2018-02-23 20:35:50
【问题描述】:

我有一个相当复杂的基于 Python 的 OpenGL 代码,它在 Windows 和 Mac 上运行良好,但在 Linux 上出现奇怪的带状球体失败。两个角度的看法:

这是同一代码在 Mac 上绘制的内容:

问题不仅在于球体,而且这是最容易展示的东西。这个问题对比我有更多 OpenGL 经验的人有什么建议吗?

感谢任何提示或建议。

这是一些显示此问题的示例代码

'''Draws a sphere and axis triplet with openGL; rotates with mouse drag.
This works fine on Windows and Mac, but sphere displays strangely on Linux
'''
import sys
import math
import numpy as np
import numpy.linalg as nl
import wx
import wx.glcanvas
import OpenGL.GL as GL
import OpenGL.GLU as GLU
drawingData = {
    'oldxy' : [0, 0],
    'Quaternion' : np.array([ 0.11783419,  0.87355958,  0.09141639,  0.4633053 ]),
    'linecolors': [(np.array([[0, 0, 0], [1, 0, 0]]), [255,   0,   0]),
                   (np.array([[0, 0, 0], [0, 1, 0]]), [  0, 255,   0]),
                   (np.array([[0, 0, 0], [0, 0, 1]]), [  0,   0, 255])],
}

def Q2Mat(Q):
    ''' make rotation matrix from quaternion
    '''
    QN = Q/np.sqrt(np.sum(np.array(Q)**2))
    aa = QN[0]**2
    ab = QN[0]*QN[1]
    ac = QN[0]*QN[2]
    ad = QN[0]*QN[3]
    bb = QN[1]**2
    bc = QN[1]*QN[2]
    bd = QN[1]*QN[3]
    cc = QN[2]**2
    cd = QN[2]*QN[3]
    dd = QN[3]**2
    M = [[aa+bb-cc-dd, 2.*(bc-ad),  2.*(ac+bd)],
        [2*(ad+bc),   aa-bb+cc-dd,  2.*(cd-ab)],
        [2*(bd-ac),    2.*(ab+cd), aa-bb-cc+dd]]
    return np.array(M)

def prodQVQ(Q,V):
    """compute the quaternion vector rotation qvq-1 = v'
    """
    T2 = Q[0]*Q[1]
    T3 = Q[0]*Q[2]
    T4 = Q[0]*Q[3]
    T5 = -Q[1]*Q[1]
    T6 = Q[1]*Q[2]
    T7 = Q[1]*Q[3]
    T8 = -Q[2]*Q[2]
    T9 = Q[2]*Q[3]
    T10 = -Q[3]*Q[3]
    M = np.array([[T8+T10,T6-T4,T3+T7],[T4+T6,T5+T10,T9-T2],[T7-T3,T2+T9,T5+T8]])
    VP = 2.*np.inner(V,M)
    return VP+V

def invQ(Q):
    '''get inverse of quaternion q=r+ai+bj+ck; q* = r-ai-bj-ck
    '''
    return Q*np.array([1,-1,-1,-1])

def AVdeg2Q(A,V):
    ''' convert angle (degrees) & vector to quaternion
        q=r+ai+bj+ck
    '''
    sind = lambda x: math.sin(x*math.pi/180.)
    cosd = lambda x: math.cos(x*math.pi/180.)
    Q = np.zeros(4)
    d = nl.norm(np.array(V))
    if not A:       #== 0.!
        A = 360.
    if d:
        V = V/d
        p = A/2.
        Q[0] = cosd(p)
        Q[1:4] = V*sind(p)
    else:
        Q[3] = 1.
    return Q

def prodQQ(QA,QB):
    ''' Grassman quaternion product, QA,QB quaternions; q=r+ai+bj+ck
    '''
    D = np.zeros(4)
    D[0] = QA[0]*QB[0]-QA[1]*QB[1]-QA[2]*QB[2]-QA[3]*QB[3]
    D[1] = QA[0]*QB[1]+QA[1]*QB[0]+QA[2]*QB[3]-QA[3]*QB[2]
    D[2] = QA[0]*QB[2]-QA[1]*QB[3]+QA[2]*QB[0]+QA[3]*QB[1]
    D[3] = QA[0]*QB[3]+QA[1]*QB[2]-QA[2]*QB[1]+QA[3]*QB[0]
    return D

def RenderUnitVectors(x,y,z):
    'Show the axes'
    GL.glEnable(GL.GL_COLOR_MATERIAL)
    GL.glLineWidth(2)
    GL.glEnable(GL.GL_BLEND)
    GL.glBlendFunc(GL.GL_SRC_ALPHA,GL.GL_ONE_MINUS_SRC_ALPHA)
    GL.glEnable(GL.GL_LINE_SMOOTH)
    GL.glPushMatrix()
    GL.glTranslate(x,y,z)
    GL.glScalef(1,1,1)
    GL.glBegin(GL.GL_LINES)
    for line,color in drawingData['linecolors']:
            GL.glColor3ubv(color)
            GL.glVertex3fv(-line[1]/2.)
            GL.glVertex3fv(line[1]/2.)
    GL.glEnd()
    GL.glPopMatrix()
    GL.glColor4ubv([0,0,0,0])
    GL.glDisable(GL.GL_LINE_SMOOTH)
    GL.glDisable(GL.GL_BLEND)
    GL.glDisable(GL.GL_COLOR_MATERIAL)

def RenderSphere(x,y,z,radius,color):
    'show a sphere'
    GL.glMaterialfv(GL.GL_FRONT_AND_BACK,GL.GL_DIFFUSE,color)
    GL.glPushMatrix()
    GL.glTranslate(x,y,z)        
    GL.glMultMatrixf(np.eye(4).T)
    GLU.gluSphere(GLU.gluNewQuadric(),radius,20,10)
    GL.glPopMatrix()

class myGLCanvas(wx.Panel):
    def __init__(self, parent, id=-1,dpi=None,**kwargs):
        wx.Panel.__init__(self,parent,id=id,**kwargs)
        if 'win' in sys.platform:           # for Windows (& darwin==Mac) -- already double buffered
            attribs = None
        else:                               # Linux
            attribs = [wx.glcanvas.WX_GL_DOUBLEBUFFER,]
        self.canvas = wx.glcanvas.GLCanvas(self,-1,attribList=attribs,**kwargs)
        self.context = wx.glcanvas.GLContext(self.canvas)
        self.canvas.SetCurrent(self.context)
        sizer=wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.canvas,1,wx.EXPAND)
        self.SetSizer(sizer)
        self.canvas.Bind(wx.EVT_MOTION, self.OnMouseMove)
        self.Draw()
        self.Draw()
        return

    def OnMouseMove(self,event):
        if not event.Dragging():
            drawingData['oldxy'] = list(event.GetPosition())
            return
        # Perform a rotation in x-y space
        oldxy = drawingData['oldxy']
        if not len(oldxy): oldxy = list(event.GetPosition())
        dxy = event.GetPosition()-oldxy
        drawingData['oldxy'] = list(event.GetPosition())
        V = np.array([dxy[1],dxy[0],0.])
        A = 0.25*np.sqrt(dxy[0]**2+dxy[1]**2)
        if not A: return
        # next transform vector back to xtal coordinates via inverse quaternion & make new quaternion
        Q = drawingData['Quaternion']
        V = prodQVQ(invQ(Q),np.inner(np.eye(3),V))
        Q = prodQQ(Q,AVdeg2Q(A,V))
        drawingData['Quaternion'] = Q
        self.Draw()

    def Draw(self):
        GL.glClearColor(0.,0.,0.,0.)
        GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
        GL.glInitNames()
        GL.glPushName(0)

        GL.glMatrixMode(GL.GL_PROJECTION)
        GL.glLoadIdentity()
        GL.glViewport(0,0,*self.canvas.GetSize())
        GLU.gluPerspective(20.,self.canvas.GetSize()[0]*1./self.canvas.GetSize()[1],7.5,12.5)
        GLU.gluLookAt(0,0,10,0,0,0,0,1,0)

        # Set Lighting            
        GL.glEnable(GL.GL_DEPTH_TEST)
        GL.glEnable(GL.GL_LIGHTING)
        GL.glEnable(GL.GL_LIGHT0)
        GL.glLightModeli(GL.GL_LIGHT_MODEL_TWO_SIDE,0)
        GL.glLightfv(GL.GL_LIGHT0,GL.GL_AMBIENT,[1,1,1,1])
        GL.glLightfv(GL.GL_LIGHT0,GL.GL_DIFFUSE,[1,1,1,1])

        GL.glMatrixMode(GL.GL_MODELVIEW)
        GL.glLoadIdentity()
        matRot = Q2Mat(drawingData['Quaternion'])
        matRot = np.concatenate((np.concatenate((matRot,[[0],[0],[0]]),axis=1),[[0,0,0,1],]),axis=0)
        GL.glMultMatrixf(matRot.T)
        GL.glMultMatrixf(np.eye(4).T)
        Tx,Ty,Tz = (0.20045985394544949, 0.44135342324377724, 0.40844172594191536)
        GL.glTranslate(-Tx,-Ty,-Tz)
        RenderUnitVectors(Tx,Ty,Tz)
        RenderSphere(0, 0, 0, 0.804, [1.,  1.,  1.])
        self.canvas.SetCurrent(self.context)
        self.canvas.SwapBuffers()

class GUI(wx.App):
    def OnInit(self):
        frame = wx.Frame(None,-1,'ball rendering',wx.DefaultPosition,wx.Size(400,400))
        frame.Show()
        wx.CallAfter(myGLCanvas,frame,size=wx.Size(400,400)) # wait for frame to be displayed
        self.MainLoop()
        return True

if __name__ == '__main__':
    GUI()

【问题讨论】:

  • 创建了一个大大缩短的代码来说明问题。尚未在 Linux 上进行全面测试(在我调试轮换之前确实出现了问题)。 FWIW,我可以通过在 Mac 上打开 WX_GL_DOUBLEBUFFER 看到类似于 Linux 问题的情况。

标签: python python-2.7 wxpython pyopengl


【解决方案1】:

您必须根据您的硬件条件,通过设置WX_GL_DEPTH_SIZE 来指定深度缓冲区的位数。深度缓冲区的大小应为 16、24 或 32。

attribs = [
     wx.glcanvas.WX_GL_RGBA,
     wx.glcanvas.WX_GL_DOUBLEBUFFER,
     wx.glcanvas.WX_GL_DEPTH_SIZE, 16]

另见:

【讨论】:

  • 这确实是问题所在!由于我想在不同的硬件上支持 wx 2.8 和不同的缓冲区深度,我将 wx.glcanvas.GLCanvas 和 wx.glcanvas.GLContext 调用放在 try/except 块内的循环中,并依次尝试 32、24 和 16 位。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多