'OPENGL gluPerspective implementation

I have a problem when I implement my own gluPerspective matrix transformation. It doesn't display anything. But when I comment out my perspective implementation, using the api, it seems like my Lookat function can work. I'm not sure where is the problem? Here is my reference for my matrix transformation: 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()

I think the problem should be the following funcion:

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

To test my code, just comment out the swPerspective function in display(), using the api. It will work.:

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()


Solution 1:[1]

The calculation of the projection matrix and view matrix is wrong. OpenGL matrices are column major order matrices. Therefor you need to transpose the matrices. For the mathematical functions, the unit of angle is radians. However, there are some other issues with your projection matrix. See OpenGL Projection Matrix. Correct matrices looks like this:

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

Additionally, you have to load the matrices using 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()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1