Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Results 1 to 6 of 6

Thread: View and Perspective matrices

  1. #1
    Junior Member Newbie
    Join Date
    Nov 2015
    Posts
    20

    Question View and Perspective matrices

    Hi!

    This week I started learning OpenGL through various tutorials. I started by drawing a 3D cube (centered in (0,0,0) whose vertices are between -1 and 1 along the three dimensions). I also managed to rotate my cube (along the three axes) i.e. modifying the Model matrix and it behaves as expected.

    Now, I would like to define View matrix and Projection matrix. However, as I am working in Python, I have tried to implement (using OpenGL source code of these functions) glLookAt(view, center, up) to yield View matrix and Perspective(GLdouble fov, GLdouble aspect, GLdouble near, GLdouble far) and glFrustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far) to yield Projection matrix.

    Nonetheless, as, currently, my functions seem to fail, I would like to know if somebody can give me what these two functions should yield (4x4 matrices), in running the C++ command:

    glLookAt(view=[0,0,1], center=[0,0,0], up=[0,1,0]) = ???
    Perspective(FoV=45, Ratio=4/3, near=0.1, far=100) = ???

    It would be very helpful to debug both functions as due to column/row major ordering, it could be necessary to transpose my Python's matrices.

    Thanks in advance.

  2. #2
    Senior Member OpenGL Guru
    Join Date
    Jun 2013
    Posts
    3,002
    Here is what I use.
    Code :
     
    import math
    import numpy as np
     
    def transform(m, v):
        return np.asarray(m * np.asmatrix(v).T)[:,0]
     
    def magnitude(v):
        return math.sqrt(np.sum(v ** 2))
     
    def normalize(v):
        m = magnitude(v)
        if m == 0:
            return v
        return v / m
     
    def ortho(l, r, b, t, n, f):
        dx = r - l
        dy = t - b
        dz = f - n
        rx = -(r + l) / (r - l)
        ry = -(t + b) / (t - b)
        rz = -(f + n) / (f - n)
        return np.matrix([[2.0/dx,0,0,rx],
                          [0,2.0/dy,0,ry],
                          [0,0,-2.0/dz,rz],
                          [0,0,0,1]])
     
    def perspective(fovy, aspect, n, f):
        s = 1.0/math.tan(math.radians(fovy)/2.0)
        sx, sy = s / aspect, s
        zz = (f+n)/(n-f)
        zw = 2*f*n/(n-f)
        return np.matrix([[sx,0,0,0],
                          [0,sy,0,0],
                          [0,0,zz,zw],
                          [0,0,-1,0]])
     
    def frustum(x0, x1, y0, y1, z0, z1):
        a = (x1+x0)/(x1-x0)
        b = (y1+y0)/(y1-y0)
        c = -(z1+z0)/(z1-z0)
        d = -2*z1*z0/(z1-z0)
        sx = 2*z0/(x1-x0)
        sy = 2*z0/(y1-y0)
        return np.matrix([[sx, 0, a, 0],
                          [ 0,sy, b, 0],
                          [ 0, 0, c, d],
                          [ 0, 0,-1, 0]])
     
    def translate(xyz):
        x, y, z = xyz
        return np.matrix([[1,0,0,x],
                          [0,1,0,y],
                          [0,0,1,z],
                          [0,0,0,1]])
     
    def scale(xyz):
        x, y, z = xyz
        return np.matrix([[x,0,0,0],
                          [0,y,0,0],
                          [0,0,z,0],
                          [0,0,0,1]])
     
    def sincos(a):
        a = math.radians(a)
        return math.sin(a), math.cos(a)
     
    def rotate(a, xyz):
        x, y, z = normalize(xyz)
        s, c = sincos(a)
        nc = 1 - c
        return np.matrix([[x*x*nc +   c, x*y*nc - z*s, x*z*nc + y*s, 0],
                          [y*x*nc + z*s, y*y*nc +   c, y*z*nc - x*s, 0],
                          [x*z*nc - y*s, y*z*nc + x*s, z*z*nc +   c, 0],
                          [           0,            0,            0, 1]])
     
    def rotx(a):
        s, c = sincos(a)
        return np.matrix([[1,0,0,0],
                          [0,c,-s,0],
                          [0,s,c,0],
                          [0,0,0,1]])
     
    def roty(a):
        s, c = sincos(a)
        return np.matrix([[c,0,s,0],
                          [0,1,0,0],
                          [-s,0,c,0],
                          [0,0,0,1]])
     
    def rotz(a):
        s, c = sincos(a)
        return np.matrix([[c,-s,0,0],
                          [s,c,0,0],
                          [0,0,1,0],
                          [0,0,0,1]])
     
    def lookat(eye, target, up):
        F = target[:3] - eye[:3]
        f = normalize(F)
        U = normalize(up[:3])
        s = np.cross(f, U)
        u = np.cross(s, f)
        M = np.matrix(np.identity(4))
        M[:3,:3] = np.vstack([s,u,-f])
        T = translate(-eye)
        return M * T
     
    def viewport(x, y, w, h):
        x, y, w, h = map(float, (x, y, w, h))
        return np.matrix([[w/2, 0  , 0,x+w/2],
                          [0  , h/2, 0,y+h/2],
                          [0  , 0  , 1,    0],
                          [0  , 0  , 0,    1]])

    Functions which are named similarly to OpenGL/GLU functions have the same conventions (and in many cases, the matrices were taken directly from the OpenGL reference pages).

    A note about NumPy matrices: if you transpose the matrix using the .transpose() method or the .T property, you need to use np.ascontiguousarray() to actually re-order the data. Alternatively, you can just use np.asfortranarray() to store the matrix in column-major order. PyOpenGL simply passes a pointer to the underlying buffer, so array views created e.g. by transposition or slicing won't behave as expected.

  3. #3
    Junior Member Newbie
    Join Date
    Nov 2015
    Posts
    20
    Quote Originally Posted by GClements View Post
    Here is what I use.
    Code :
     
    import math
    import numpy as np
     
    def transform(m, v):
        return np.asarray(m * np.asmatrix(v).T)[:,0]
     
    def magnitude(v):
        return math.sqrt(np.sum(v ** 2))
     
    def normalize(v):
        m = magnitude(v)
        if m == 0:
            return v
        return v / m
     
    def ortho(l, r, b, t, n, f):
        dx = r - l
        dy = t - b
        dz = f - n
        rx = -(r + l) / (r - l)
        ry = -(t + b) / (t - b)
        rz = -(f + n) / (f - n)
        return np.matrix([[2.0/dx,0,0,rx],
                          [0,2.0/dy,0,ry],
                          [0,0,-2.0/dz,rz],
                          [0,0,0,1]])
     
    def perspective(fovy, aspect, n, f):
        s = 1.0/math.tan(math.radians(fovy)/2.0)
        sx, sy = s / aspect, s
        zz = (f+n)/(n-f)
        zw = 2*f*n/(n-f)
        return np.matrix([[sx,0,0,0],
                          [0,sy,0,0],
                          [0,0,zz,zw],
                          [0,0,-1,0]])
     
    def frustum(x0, x1, y0, y1, z0, z1):
        a = (x1+x0)/(x1-x0)
        b = (y1+y0)/(y1-y0)
        c = -(z1+z0)/(z1-z0)
        d = -2*z1*z0/(z1-z0)
        sx = 2*z0/(x1-x0)
        sy = 2*z0/(y1-y0)
        return np.matrix([[sx, 0, a, 0],
                          [ 0,sy, b, 0],
                          [ 0, 0, c, d],
                          [ 0, 0,-1, 0]])
     
    def translate(xyz):
        x, y, z = xyz
        return np.matrix([[1,0,0,x],
                          [0,1,0,y],
                          [0,0,1,z],
                          [0,0,0,1]])
     
    def scale(xyz):
        x, y, z = xyz
        return np.matrix([[x,0,0,0],
                          [0,y,0,0],
                          [0,0,z,0],
                          [0,0,0,1]])
     
    def sincos(a):
        a = math.radians(a)
        return math.sin(a), math.cos(a)
     
    def rotate(a, xyz):
        x, y, z = normalize(xyz)
        s, c = sincos(a)
        nc = 1 - c
        return np.matrix([[x*x*nc +   c, x*y*nc - z*s, x*z*nc + y*s, 0],
                          [y*x*nc + z*s, y*y*nc +   c, y*z*nc - x*s, 0],
                          [x*z*nc - y*s, y*z*nc + x*s, z*z*nc +   c, 0],
                          [           0,            0,            0, 1]])
     
    def rotx(a):
        s, c = sincos(a)
        return np.matrix([[1,0,0,0],
                          [0,c,-s,0],
                          [0,s,c,0],
                          [0,0,0,1]])
     
    def roty(a):
        s, c = sincos(a)
        return np.matrix([[c,0,s,0],
                          [0,1,0,0],
                          [-s,0,c,0],
                          [0,0,0,1]])
     
    def rotz(a):
        s, c = sincos(a)
        return np.matrix([[c,-s,0,0],
                          [s,c,0,0],
                          [0,0,1,0],
                          [0,0,0,1]])
     
    def lookat(eye, target, up):
        F = target[:3] - eye[:3]
        f = normalize(F)
        U = normalize(up[:3])
        s = np.cross(f, U)
        u = np.cross(s, f)
        M = np.matrix(np.identity(4))
        M[:3,:3] = np.vstack([s,u,-f])
        T = translate(-eye)
        return M * T
     
    def viewport(x, y, w, h):
        x, y, w, h = map(float, (x, y, w, h))
        return np.matrix([[w/2, 0  , 0,x+w/2],
                          [0  , h/2, 0,y+h/2],
                          [0  , 0  , 1,    0],
                          [0  , 0  , 0,    1]])

    Functions which are named similarly to OpenGL/GLU functions have the same conventions (and in many cases, the matrices were taken directly from the OpenGL reference pages).

    A note about NumPy matrices: if you transpose the matrix using the .transpose() method or the .T property, you need to use np.ascontiguousarray() to actually re-order the data. Alternatively, you can just use np.asfortranarray() to store the matrix in column-major order. PyOpenGL simply passes a pointer to the underlying buffer, so array views created e.g. by transposition or slicing won't behave as expected.

    I tried the lookat and perspective functions but, now, my cube looks like a pyramid and I do not know what's causing this.
    Here is the code of my timer function:

    Code :
     def timer(fps):
        global clock
        clock += 0.0005*1000.0/fps
     
        theta = clock;
        matModel = rotate(theta*180/np.pi, np.array([1,1,1]))
     
        eye = np.array([0,0,1])
        target = np.array([0,0,0])
        up = np.array([0,1,0])    
     
        matView = lookat(eye, target, up)
        #matView = np.eye(4)
     
        matProjection = perspective(fovy=45.0, aspect=4/3, n=-3, f=100.0)
        #matProjection = np.eye(4)  
     
        loc = gl.glGetUniformLocation(program, "Model")
        gl.glUniformMatrix4fv(loc, 1, False, np.asfortranarray(matModel))
     
        loc = gl.glGetUniformLocation(program, "View")
        gl.glUniformMatrix4fv(loc, 1, False, np.asfortranarray(matView))
     
        loc = gl.glGetUniformLocation(program, "Projection")
        gl.glUniformMatrix4fv(loc, 1, False, np.asfortranarray(matProjection))                 
     
     
        glut.glutTimerFunc(1000/fps, timer, fps)
        glut.glutPostRedisplay()

    My display function:

    Code :
     
    def display():
     
        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
        #gl.glDrawArrays(gl.GL_TRIANGLES, 0, 12)
     
        gl.glEnable(gl.GL_DEPTH_TEST)
        gl.glDepthFunc(gl.GL_LESS)
     
        gl.glDrawElements(gl.GL_TRIANGLES, len(index), gl.GL_UNSIGNED_INT, ctypes.c_void_p(0))
        glut.glutSwapBuffers()

    The vertices and indices of my cube:

    Code :
    positions = [ (-1,-1,1),
                         (1,-1,1),
                            (1,1,1),   
                            (-1,1,1),
                            (-1,-1,-1),
                            (1,-1,-1),
                            (1,1,-1),
                            (-1,1,-1)]
     
    index = np.array([0,1,2,
                    2,3,0,
                    1,5,6,
                    6,2,1,
                    7,6,5,
                    5,4,7,
                    4,0,3,
                    3,7,4,
                    4,5,1,
                    1,0,4,
                    3,2,6,
                    6,7,3], dtype=np.uint32)

    and my shaders:
    Code :
     
    vertex_code = """
        #version 330 core
        uniform float scale;
        uniform mat4 Model;
        uniform mat4 View;
        uniform mat4 Projection;
        in vec4 color;
        in vec3 position;
        out vec4 v_color;
        void main()
        {
            gl_Position = Projection*View*Model*vec4(scale*position, 1.0);
            v_color = color;
        } """
     
    fragment_code = """
        #version 330 core
        in vec4 v_color;
        void main()
        {
            gl_FragColor = v_color;
        } """
    Last edited by neon29; 11-30-2015 at 04:15 AM.

  4. #4
    Senior Member OpenGL Guru
    Join Date
    Jun 2013
    Posts
    3,002
    Quote Originally Posted by neon29 View Post
    Code :
        eye = np.array([0,0,1])
        target = np.array([0,0,0])
        up = np.array([0,1,0])
    I suggest making the eye position farther from the cube. [0,0,1] will be either on the surface of the cube or inside it.

    Quote Originally Posted by neon29 View Post
    Code :
        matProjection = perspective(fovy=45.0, aspect=4/3, n=-3, f=100.0)
    The near and far values should both be positive, and the near value shouldn't be too small (too small a near value will reduce the depth precision over most of the scene). n=0.1, f=10.0 should be sufficient here.

    Also, if you're using Python 2.x (and aren't using "from __future__ import division"), aspect=4/3 is equivalent to aspect=1, as dividing integers yields an integer result. If you're using Python 3 ... I haven't tested my code with Python 3.

    Quote Originally Posted by neon29 View Post
    Code :
        gl.glUniformMatrix4fv(loc, 1, False, np.asfortranarray(matProjection))
    I suggest adding "dtype=np.float32" to the np.asfortranarray() call. NumPy arrays normally use double precision.

  5. #5
    Junior Member Newbie
    Join Date
    Nov 2015
    Posts
    20
    Quote Originally Posted by GClements View Post
    I suggest making the eye position farther from the cube. [0,0,1] will be either on the surface of the cube or inside it.


    The near and far values should both be positive, and the near value shouldn't be too small (too small a near value will reduce the depth precision over most of the scene). n=0.1, f=10.0 should be sufficient here.

    Also, if you're using Python 2.x (and aren't using "from __future__ import division"), aspect=4/3 is equivalent to aspect=1, as dividing integers yields an integer result. If you're using Python 3 ... I haven't tested my code with Python 3.


    I suggest adding "dtype=np.float32" to the np.asfortranarray() call. NumPy arrays normally use double precision.
    I also remember that 4/3 yields 1 so I immediatly correct it once I answered . I also find out what are the best parameters to get what I want.

    However the main issue was that I had to transpose the three matrices (Model, View and Projection) or to use "True" as the second parameters in glUniformMatrix4fv().

    However, the result (which is the expected one) does not change wether I use np.asarray, np.contiguousarray or np.asfortranarray, why?

  6. #6
    Senior Member OpenGL Guru
    Join Date
    Jun 2013
    Posts
    3,002
    Quote Originally Posted by neon29 View Post
    However, the result (which is the expected one) does not change wether I use np.asarray, np.contiguousarray or np.asfortranarray, why?
    It appears that the behaviour has changed between versions. For my tests (PyOpenGL 3.0.2, NumPy 1.6.2), it matters whether I use ascontiguousarray() or asfortranarray(), but transposing the array returned from ascontiguousarray() or asfortranarray() has no effect (i.e. it's clearly using the in-memory layout directly).

    For me, these all produce the correct result:
    Code :
    glUniformMatrix4fv(loc, 1, GL_TRUE , np.ascontiguousarray(m, dtype=np.float32))
    glUniformMatrix4fv(loc, 1, GL_FALSE, np.ascontiguousarray(m.T, dtype=np.float32))
    glUniformMatrix4fv(loc, 1, GL_FALSE, np.asfortranarray(m, dtype=np.float32))
    glUniformMatrix4fv(loc, 1, GL_TRUE , np.asfortranarray(m.T, dtype=np.float32))

    while these all produce the wrong result:
    Code :
    glUniformMatrix4fv(loc, 1, GL_FALSE, np.ascontiguousarray(m, dtype=np.float32))
    glUniformMatrix4fv(loc, 1, GL_TRUE , np.ascontiguousarray(m.T, dtype=np.float32))
    glUniformMatrix4fv(loc, 1, GL_TRUE , np.asfortranarray(m, dtype=np.float32))
    glUniformMatrix4fv(loc, 1, GL_FALSE, np.asfortranarray(m.T, dtype=np.float32))

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •