OPENGL glu透视实现

OPENGL gluPerspective implementation

我在实现自己的 gluPerspective 矩阵变换时遇到问题。它不显示任何内容。但是当我注释掉我的透视图实现时,使用 api,我的 Lookat 函数似乎可以工作。我不确定问题出在哪里? 这是我对矩阵变换的参考: http://www.3dcpptutorials.sk/index.php?id=2

import sys, os
from math import pi as PI
from math import sin, cos
import math 


from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

import glfw
import numpy as np

def vec3(xx,yy,zz):
    temp=[]
    temp.append(xx)
    temp.append(yy)
    temp.append(zz)

    return temp

def vec4(xx,yy,zz,dd):
    temp=[]
    temp.append(xx)
    temp.append(yy)
    temp.append(zz)
    temp.append(dd)

    return temp

########################################

class RenderWindow:
    def __init__(self):
        self.STEP2 = True
        self.STEP3 = True

        #########################################################
        glfw.init()
        self.width, self.height = 640, 480
        self.win =glfw.create_window(self.width, self.height, "HW-SWGL-trans", None, None)
        glfw.set_key_callback(self.win, self.on_key)
        glfw.make_context_current(self.win)
        
        glClearColor(0, 0, 0, 0)
        glClearDepth(1.0)
        glEnable(GL_DEPTH_TEST)
        glDepthFunc(GL_LEQUAL)

        self.exitNow = False
        self.transformMat=np.identity(4)
        self.ViewMat = np.identity(4)
        self.ProjectionMat = np.identity(4)

        self.winWidth = 1280
        self.winHeight = 720
        
        self.tho = 3.14159/4.0
        self.theta = 3.14159/4.0

        self.tetrahedron_verts=[]
        for k in range(4):
            self.tetrahedron_verts.append(vec3(0,0,0))

        self.default_tetrahedron_vertices=[]
        self.default_tetrahedron_vertices.append(vec3(1,0,0))
        self.default_tetrahedron_vertices.append(vec3(0,1,0))
        self.default_tetrahedron_vertices.append(vec3(0,0,1))
        self.default_tetrahedron_vertices.append(vec3(0,0,0))

        self.tetrahedron_verts=self.default_tetrahedron_vertices
        

    def on_key(self, win, key, scancode, action, mods):
        if action == glfw.PRESS:
            if key == glfw.KEY_ESCAPE:
                self.exitNow = True 

            if (key==glfw.KEY_Q):
                glfw.set_window_title(self.win, "translate +x")
                self.transformMat=np.matmul(self.swTranslate(1, 0, 0), self.transformMat)   

            if (key==glfw.KEY_A):
                glfw.set_window_title(self.win, "translate -x")
                self.transformMat=np.matmul(self.swTranslate(-1, 0, 0), self.transformMat)

            if (key==glfw.KEY_W):
                glfw.set_window_title(self.win, "translate +y")
                self.transformMat=np.matmul(self.swTranslate(0, 1, 0), self.transformMat)

            if (key==glfw.KEY_S):
                glfw.set_window_title(self.win, "translate -y")
                self.transformMat=np.matmul(self.swTranslate(0, -1, 0), self.transformMat)

            if (key==glfw.KEY_E):
                glfw.set_window_title(self.win, "translate +z")
                self.transformMat=np.matmul(self.swTranslate(0, 0, 1), self.transformMat)

            if (key==glfw.KEY_D):
                glfw.set_window_title(self.win, "translate -z")
                self.transformMat=np.matmul(self.swTranslate(0, 0, -1), self.transformMat)

            if (key==glfw.KEY_R):
                glfw.set_window_title(self.win, "rotate +x")
                self.transformMat=np.matmul(self.swRotateX(self.tho), self.transformMat)    

            if (key==glfw.KEY_F):
                glfw.set_window_title(self.win, "rotate -x")
                self.transformMat=np.matmul(self.swRotateX(-self.tho), self.transformMat)

            if (key==glfw.KEY_T):
                glfw.set_window_title(self.win, "rotate +y")
                self.transformMat=np.matmul(self.swRotateY(self.tho), self.transformMat)

            if (key==glfw.KEY_G):
                glfw.set_window_title(self.win, "rotate -y")
                self.transformMat=np.matmul(self.swRotateY(-self.tho), self.transformMat)

            if (key==glfw.KEY_Y):
                glfw.set_window_title(self.win, "rotate +z")
                self.transformMat=np.matmul(self.swRotateZ(self.tho), self.transformMat)

            if (key==glfw.KEY_H):
                glfw.set_window_title(self.win, "rotate -z")
                self.transformMat=np.matmul(self.swRotateZ(-self.tho), self.transformMat)

            if (key==glfw.KEY_U):
                glfw.set_window_title(self.win, "scale +x")
                self.transformMat=np.matmul(self.swScale(2,1,1), self.transformMat)

            if (key==glfw.KEY_J):
                glfw.set_window_title(self.win, "scale -x")
                self.transformMat=np.matmul(self.swScale(1/2,1,1), self.transformMat)

            if (key==glfw.KEY_I):
                glfw.set_window_title(self.win, "scale +y")
                self.transformMat=np.matmul(self.swScale(1,2,1), self.transformMat)

            if (key==glfw.KEY_K):
                glfw.set_window_title(self.win, "scale -y")
                self.transformMat=np.matmul(self.swScale(1,1/2,1), self.transformMat)

            if (key==glfw.KEY_O):
                glfw.set_window_title(self.win, "scale +z")
                self.transformMat=np.matmul(self.swScale(1,1,2), self.transformMat)

            if (key==glfw.KEY_L):
                glfw.set_window_title(self.win, "scale -z")
                self.transformMat=np.matmul(self.swScale(1,1,1/2), self.transformMat)
                
            #####################################################
            if (key==glfw.KEY_F1): #you have to press fn and f1 at the same time
                glfw.set_window_title(self.win, "F1: add a tetrahedron")
                for i in range(4):
                    self.tetrahedron_verts[i] = self.default_tetrahedron_vertices[i]

            if (key==glfw.KEY_F2):
                glfw.set_window_title(self.win, "F2: add a cube or somthing")

            if (key==glfw.KEY_F5):
                glfw.set_window_title(self.win, "F5: SAVE")

            if (key==glfw.KEY_F6):
                glfw.set_window_title(self.win, "F6: LOAD")

            if (key==glfw.KEY_9):
                self.theta+=3.14159/90

            if (key==glfw.KEY_0):
                self.theta+=(-3.14159/90)

            if (key==glfw.KEY_MINUS):
                self.transformMat=np.identity(4)

    def normal(self,vv):
        norm=math.sqrt((vv[0])**2+(vv[1])**2+(vv[2])**2)
        fin=np.true_divide(vv, norm)
        
        return fin


    def swScale(self,x,y,z):
        Scale = np.identity(4)
        Scale[0][0]=x
        Scale[1][1]=y
        Scale[2][2]=z

        return Scale

    def swRotate(self,angle,x,y,z):
        Rotate = np.identity(4)
        Rotate[0][0]=cos(angle)+pow(x,2)*(1-cos(angle))
        Rotate[0][1]=x*y*(1-cos(angle))-z*sin(angle)
        Rotate[0][2]=x*z*(1-cos(angle))+y*sin(angle)
        Rotate[1][0]=y*x*(1-cos(angle))+z*sin(angle)
        Rotate[1][1]=cos(angle)+pow(y,2)*(1-cos(angle))
        Rotate[1][2]=y*z*(1-cos(angle))-x*sin(angle)
        
        Rotate[2][0]=z*x*(1-cos(angle))-y*sin(angle)
        Rotate[2][1]=z*y*(1-cos(angle))+x*sin(angle)
        Rotate[2][2]=cos(angle)+pow(z,2)*(1-cos(angle))
        

        return Rotate

    def swRotateZ(self,angle):
        Rotate = np.identity(4)
        Rotate[0][0]=cos(angle)
        Rotate[0][1]=-sin(angle)
        Rotate[1][0]=sin(angle)
        Rotate[1][1]=cos(angle)

        return Rotate

    def swRotateY(self,angle):
        Rotate = np.identity(4)

        Rotate[0][0]=cos(angle)
        Rotate[0][2]=sin(angle)
        Rotate[2][0]=-sin(angle)
        Rotate[2][2]=cos(angle)

        return Rotate

    def swRotateX(self,angle):
        Rotate = np.identity(4)
        Rotate[1][1]=cos(angle)
        Rotate[1][2]=-sin(angle)
        Rotate[2][1]=sin(angle)
        Rotate[2][2]=cos(angle)


        return Rotate

    def swTranslate(self,x,y,z):
        Translate = np.identity(4)
        #Translate[3][0] = x
        #//todo: y z
        Translate[0][3] = x
        Translate[1][3] = y
        Translate[2][3] = z

        return Translate


    def drawgrid(self):
        size=10
        glBegin(GL_LINES)

        glColor3f(0.3, 0.3, 0.3)
        for i in range(1,10):
            
            self.drawp(i,-size,0)
            self.drawp(i,size,0)
            self.drawp(-i, -size, 0)
            self.drawp(-i, size, 0)

            self.drawp(-size, i, 0)
            self.drawp(size, i, 0)
            self.drawp(-size, -i, 0)
            self.drawp(size, -i, 0)
        
        glEnd()

        glBegin(GL_LINES)
        glColor3f(1, 0, 0)
        self.drawp(0, 0, 0)
        self.drawp(size, 0, 0)
        
        glColor3f(0.4, 0, 0)
        self.drawp(0, 0, 0)
        self.drawp(-size, 0, 0)
        

        glColor3f(0, 1, 0)
        self.drawp(0, 0, 0)
        self.drawp(0, size, 0)

        glColor3f(0, 0.4, 0)
        self.drawp(0, 0, 0)
        self.drawp(0, -size, 0)

        glColor3f(0, 0, 1)
        self.drawp(0, 0, 0)
        self.drawp(0, 0, size)

        glEnd()

    def drawp(self,p1,p2,p3):
        #self.ProjectionMat
        #np.array([p1, p2, p3, 1])
        #self.ViewMat

        tt=np.matmul(self.ProjectionMat,self.ViewMat)
        tt=np.matmul(tt,np.array([p1, p2, p3, 1]))
        glVertex3f(tt[0],tt[1],tt[2])

    def Draw_Tetrahedron(self):
        #vec3 color(1, 1, 0)
        glColor3f(1, 1, 0)
        glBegin(GL_TRIANGLES)

        self.swTriangle(vec3(1, 0, 0), self.tetrahedron_verts[0], self.tetrahedron_verts[1], self.tetrahedron_verts[2], self.transformMat)
        self.swTriangle(vec3(0, 0, 1), self.tetrahedron_verts[3], self.tetrahedron_verts[0], self.tetrahedron_verts[1], self.transformMat)
        self.swTriangle(vec3(0, 1, 0), self.tetrahedron_verts[2], self.tetrahedron_verts[3], self.tetrahedron_verts[0], self.transformMat)
        self.swTriangle(vec3(1, 1, 0), self.tetrahedron_verts[1], self.tetrahedron_verts[2], self.tetrahedron_verts[3], self.transformMat)

        glEnd()

    def swTriangle(self,color,in_v1,in_v2,in_v3,Modelmatrix):
        
        v1=vec4(in_v1[0], in_v1[1], in_v1[2], 1)
        v2=vec4(in_v2[0], in_v2[1], in_v2[2], 1)
        v3=vec4(in_v3[0], in_v3[1], in_v3[2], 1)

        ##########################################
        v1 = np.matmul(Modelmatrix,np.array(v1))
        v2 = np.matmul(Modelmatrix,np.array(v2))
        v3 = np.matmul(Modelmatrix,np.array(v3))

        #step2: remove glLookAt, compute view matrix
        v1 = np.matmul(self.ViewMat,v1)
        v2 = np.matmul(self.ViewMat,v2)
        v3 = np.matmul(self.ViewMat,v3)

        #step3: remove glProjection, compute project matrix
        #v1 =  Projection * View * Modelmatrix * v1;
        #prespective division
        v1=np.matmul(self.ProjectionMat,v1)
        v2=np.matmul(self.ProjectionMat,v2)
        v3=np.matmul(self.ProjectionMat,v3)
        ##########################################
        

        glColor3f(color[0], color[1], color[2])
        glVertex3f(v1[0], v1[1], v1[2])
        glVertex3f(v2[0], v2[1], v2[2])
        glVertex3f(v3[0], v3[1], v3[2])


    def swLookAt(self,eyex,eyey,eyez,atx,aty,atz,upx,upy,upz):

        fx=atx-eyex
        fy=aty-eyey
        fz=atz-eyez

        f=[]
        f.append(fx)
        f.append(fy)
        f.append(fz)

        f=np.array(f)

        f=self.normal(f)

        up=[]
        up.append(upx)
        up.append(upy)
        up.append(upz)

        up=np.array(up)

        up=self.normal(up)

        s=np.cross(f, up)

        s=self.normal(s)
        u=np.cross(s, f)

        M=np.identity(4)
        T=np.identity(4)

        M[0][0]=s[0]
        M[0][1]=s[1]
        M[0][2]=s[2]

        M[1][0]=u[0]
        M[1][1]=u[1]
        M[1][2]=u[2]

        M[2][0]=-f[0]
        M[2][1]=-f[1]
        M[2][2]=-f[2]

        T[0][3]=-eyex
        T[1][3]=-eyey
        T[2][3]=-eyez

        mylookat=np.matmul(M,T)

        return mylookat

    def run(self):
        t = 0.0
        while not glfw.window_should_close(self.win) and not self.exitNow:
            currT = GLUT.glutGet(GLUT_ELAPSED_TIME)

            if currT - t > 0.1:
                t = currT
                self.display()
                glfw.swap_buffers(self.win)
                glfw.poll_events()


        glfw.terminate()


    def cot(self,x):
        w1=sin(x)
        w2=cos(x)

        return (w2/w1)

    def tan(self,x):
        w1=sin(x)
        w2=cos(x)

        return (w1/w2) 


    def swPerspective(self,fovy,aspect,near,far):
        per=np.zeros((4,4))

        #fovx=2*math.atan((aspect*self.tan(fovy/2)))
        gg=(fovy/2)
        #gg=math.radians(gg)
        f=self.cot(gg)

        per[0][0]=f/aspect
        per[1][1]=f
        per[2][2]=(near+far)/(near-far)
        per[2][3]=(2*near*far)/(near-far)
        per[3][2]=(-1)


        return per


    def display(self):
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

        if self.STEP2:
            #glMatrixMode(GL_MODELVIEW)
            #glLoadIdentity()
            ##gluLookAt(10*cos(self.theta), -10*sin(self.theta), 10, 0, 0, 0, 0, 0, 1)
            self.ViewMat=self.swLookAt(10 * cos(self.theta), -10 * sin(self.theta), 10, 0, 0, 0, 0, 0, 1)

        if self.STEP3:
            #glMatrixMode(GL_PROJECTION)
            #glLoadIdentity()
            #glOrtho(0, self.winWidth, 0, self.winHeight, -2.0, 2.0)
            #gluPerspective(60, 1, 0.1, 50)
            self.ProjectionMat=self.swPerspective(60, 1, 0.1, 50)       


        self.drawgrid()
        self.Draw_Tetrahedron()


def main():
    rw = RenderWindow()
    rw.run()
if __name__ == "__main__":
    main()

我认为问题应该出在以下函数上:

def drawp(self,p1,p2,p3):
        #self.ProjectionMat
        #np.array([p1, p2, p3, 1])
        #self.ViewMat

        tt=np.matmul(self.ProjectionMat,self.ViewMat)
        tt=np.matmul(tt,np.array([p1, p2, p3, 1]))
        glVertex3f(tt[0],tt[1],tt[2])


def swPerspective(self,fovy,aspect,near,far):
        per=np.zeros((4,4))

        #fovx=2*math.atan((aspect*self.tan(fovy/2)))
        gg=(fovy/2)
        #gg=math.radians(gg)
        f=self.cot(gg)

        per[0][0]=f/aspect
        per[1][1]=f
        per[2][2]=(near+far)/(near-far)
        per[2][3]=(2*near*far)/(near-far)
        per[3][2]=(-1)


        return per

要测试我的代码,只需注释掉 display() 中的 swPerspective 函数,使用 api。它会起作用。:

def display(self):
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

        if self.STEP2:
            #glMatrixMode(GL_MODELVIEW)
            #glLoadIdentity()
            #gluLookAt(10*cos(self.theta), -10*sin(self.theta), 10, 0, 0, 0, 0, 0, 1)
            self.ViewMat=self.swLookAt(10 * cos(self.theta), -10 * sin(self.theta), 10, 0, 0, 0, 0, 0, 1)

        if self.STEP3:
            glMatrixMode(GL_PROJECTION)
            glLoadIdentity()
            #glOrtho(0, self.winWidth, 0, self.winHeight, -2.0, 2.0)
            gluPerspective(60, 1, 0.1, 50)
            #self.ProjectionMat=self.swPerspective(60, 1, 0.1, 50)      


        self.drawgrid()
        self.Draw_Tetrahedron()

投影矩阵和视图矩阵计算错误。 OpenGL 矩阵是列主序矩阵。因此,您需要转置矩阵。对于数学函数,角度的单位是弧度。但是,您的投影矩阵还有一些其他问题。参见 OpenGL Projection Matrix。正确的矩阵如下所示:

def swLookAt(self,eyex,eyey,eyez,atx,aty,atz,upx,upy,upz):
    # [...]

    mylookat=np.matmul(M,T).transpose()
    return mylookat
def swPerspective(self,fovy,aspect,near,far):
        per=np.zeros((4,4))
        f = math.tan(math.radians(fovy)/2)
        per[0][0]= 1 / (f*aspect)
        per[1][1]= 1 / f
        per[2][2]=-(near+far)/(near-far)
        per[3][2]=-(2*near*far)/(near-far)
        per[2][3]=(-1)
        return per

此外,您必须使用 glLoadMatrixf:

加载矩阵
def display(self):
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    if self.STEP2:
        glMatrixMode(GL_MODELVIEW)
        glLoadMatrixf(self.swLookAt(10 * cos(self.theta), -10 * sin(self.theta), 10, 0, 0, 0, 0, 0, 1)) 

    if self.STEP3:
        glMatrixMode(GL_PROJECTION)
        glLoadMatrixf(self.swPerspective(60, 1, 0.1, 50))     

    self.drawgrid()
    self.Draw_Tetrahedron()