Python:在OpenGL Superbible示例中如何使多维数据集旋转和移动

时间:2019-06-21 02:56:01

标签: python numpy opengl pyopengl

由于某种原因,尽管立方体旋转,但它不会在屏幕上移动。

这是使用功能m3dTranslateMatrix44m3dRotationMatrix44的方法,尽管似乎有更好的方法。

修改后的 rotation_matrix(axis, theta) 有望正确生成4x4矩阵。

我认为也许是通过使用numpy乘法来创建mv_matrix。做到了。但是还是有点。

更新-2019年6月24日:经过Rabbid76的一些解释和出色的代码后,该程序现已按预期运行。旋转并在多维数据集的屏幕周围移动。很好!

#!/usr/bin/python3

import sys

import time
import math
fullscreen = True

# sys.path.append("../shared")

# from math3d import m3dDegToRad, m3dRotationMatrix44, M3DMatrix44f, m3dLoadIdentity44, \
                                            # m3dTranslateMatrix44, m3dScaleMatrix44, \
                                            # m3dMatrixMultiply44, m3dTransposeMatrix44, \
                                            # m3dRadToDeg

import numpy.matlib 
import numpy as np 

try:
    from OpenGL.GLUT import *
    from OpenGL.GL import *
    from OpenGL.GLU import *
    from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, \
                                                  glBindVertexArray
except:
    print ('''
    ERROR: PyOpenGL not installed properly.
        ''')
    sys.exit()

from math import cos, sin
from array import array

M3D_PI = 3.14159265358979323846
M3D_PI_DIV_180 = M3D_PI / 180.0
M3D_INV_PI_DIV_180 = 57.2957795130823229

# Translate matrix. Only 4x4 matrices supported
def m3dTranslateMatrix44(m, x, y, z):
    m[12] += x
    m[13] += y
    m[14] += z

def m3dDegToRad(num):
    return (num * M3D_PI_DIV_180)

def m3dRadToDeg(num):
    return (num * M3D_INV_PI_DIV_180)

def m3dOrtho(l, r, t, b, n, f):
    return (GLfloat * 16)(
        2/(r-l),      0,            0,            0,
        0,            2/(t-b),      0,            0,
        0,            0,            -2/(f-n),     0,
        -(r+l)/(r-l), -(t+b)/(t-b), -(f+n)/(f-n), 1)

def m3dPerspective(fov_y, aspect, n, f):
    a = aspect
    ta = math.tan( fov_y / 2 )
    return (GLfloat * 16)(
        1/(ta*a),  0,     0,              0,
        0,         1/ta,  0,              0,
        0,         0,    -(f+n)/(f-n),   -1,
        0,         0,    -2*f*n/(f-n),    0)

# Creates a 4x4 rotation matrix, takes radians NOT degrees
def m3dRotationMatrix44(m, angle, x, y, z):
    s = sin(angle)
    c = cos(angle)
    mag = float((x * x + y * y + z * z) ** 0.5)

    if mag == 0.0:
        m3dLoadIdentity(m)
        return

    x /= mag
    y /= mag
    z /= mag

    xx = x * x
    yy = y * y
    zz = z * z
    xy = x * y
    yz = y * z
    zx = z * x
    xs = x * s
    ys = y * s
    zs = z * s
    one_c = 1.0 - c

    m[0] = (one_c * xx) + c
    m[1] = (one_c * xy) - zs
    m[2] = (one_c * zx) + ys
    m[3] = 0.0

    m[4] = (one_c * xy) + zs
    m[5] = (one_c * yy) + c
    m[6] = (one_c * yz) - xs
    m[7] = 0.0

    m[8] = (one_c * zx) - ys
    m[9] = (one_c * yz) + xs
    m[10] = (one_c * zz) + c
    m[11]  = 0.0

    m[12] = 0.0
    m[13] = 0.0
    m[14] = 0.0
    m[15] = 1.0

def m3dMultiply(A, B):
    C = (GLfloat * 16)(*identityMatrix)
    for k in range(0, 4):
        for j in range(0, 4):
            C[k*4+j] = A[0*4+j] * B[k*4+0] + A[1*4+j] * B[k*4+1] + \
                       A[2*4+j] * B[k*4+2] + A[3*4+j] * B[k*4+3]
    return C

def translate(tx, ty, tz):
    """creates the matrix equivalent of glTranslate"""
    return np.array([1.0, 0.0, 0.0, 0.0, 
                     0.0, 1.0, 0.0, 0.0, 
                     0.0, 0.0, 1.0, 0.0, 
tx, ty, tz, 1.0], np.float32)

def rotation_matrix(axis, theta):
    """
    Return the rotation matrix associated with counterclockwise rotation about
    the given axis by theta radians.
    """
    axis = np.asarray(axis)
    axis = axis / math.sqrt(np.dot(axis, axis))
    a = math.cos(theta / 2.0)
    b, c, d = -axis * math.sin(theta / 2.0)
    aa, bb, cc, dd = a * a, b * b, c * c, d * d
    bc, ad, ac, ab, bd, cd = b * c, a * d, a * c, a * b, b * d, c * d
    return np.array([[aa + bb - cc - dd, 2 * (bc + ad), 2 * (bd - ac), 0],
                     [2 * (bc - ad), aa + cc - bb - dd, 2 * (cd + ab), 0],
                     [2 * (bd + ac), 2 * (cd - ab), aa + dd - bb - cc, 0],
[0,0,0,1]])

identityMatrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]

mv_location = (GLfloat * 16)(*identityMatrix)
proj_location = (GLfloat * 16)(*identityMatrix)
proj_matrix = (GLfloat * 16)(*identityMatrix)

many_cubes = False

# Vertex program
vs_source = '''
    #version 410 core                                                  
    in vec4 position;                                                  
    out VS_OUT                                                         
    {                                                                  
        vec4 color;                                                    
    } vs_out;                                                          
    uniform mat4 mv_matrix;                                            
    uniform mat4 proj_matrix;                                          
    void main(void)                                                    
    {                                                                  
        gl_Position = proj_matrix * mv_matrix * position;              
        vs_out.color = position * 2.0 + vec4(0.5, 0.5, 0.5, 0.0);      
    }                                                                  
'''

# Fragment program
fs_source = '''
    #version 410 core                                                  
    out vec4 color;                                                    
    in VS_OUT                                                          
    {                                                                  
        vec4 color;                                                    
    } fs_in;                                                           
    void main(void)                                                    
    {                                                                  
        color = fs_in.color;                                           
    }                                                                  
'''


def compile_program(vertex_source, fragment_source):

    global mv_location
    global proj_location

    vertex_shader = None
    fragment_shader = None

    if vertex_source:

        vertex_shader = glCreateShader(GL_VERTEX_SHADER)
        glShaderSource(vertex_shader, vertex_source)
        glCompileShader(vertex_shader)

        if not glGetShaderiv(vertex_shader, GL_COMPILE_STATUS):
                    raise Exception('failed to compile shader "%s":\n%s' % 
                    ('vertex_shader', glGetShaderInfoLog(vertex_shader)))

    if fragment_source:

        fragment_shader = glCreateShader(GL_FRAGMENT_SHADER)
        glShaderSource(fragment_shader, fragment_source)
        glCompileShader(fragment_shader)

        if not glGetShaderiv(fragment_shader, GL_COMPILE_STATUS):
                    raise Exception('failed to compile shader "%s":\n%s' % 
                    ('fragment_shader', glGetShaderInfoLog(fragment_shader)))

    program = glCreateProgram()

    glAttachShader(program, vertex_shader)
    glAttachShader(program, fragment_shader)

    glLinkProgram(program)

    mv_location = glGetUniformLocation(program, "mv_matrix");
    proj_location = glGetUniformLocation(program, "proj_matrix");

    vao = GLuint(0)
    glGenVertexArrays(1, vao);
    glBindVertexArray(vao);

    vertex_positions = [
        -0.25,  0.25, -0.25,
        -0.25, -0.25, -0.25,
         0.25, -0.25, -0.25,

         0.25, -0.25, -0.25,
         0.25,  0.25, -0.25,
        -0.25,  0.25, -0.25,

         0.25, -0.25, -0.25,
         0.25, -0.25,  0.25,
         0.25,  0.25, -0.25,

         0.25, -0.25,  0.25,
         0.25,  0.25,  0.25,
         0.25,  0.25, -0.25,

         0.25, -0.25,  0.25,
        -0.25, -0.25,  0.25,
         0.25,  0.25,  0.25,

        -0.25, -0.25,  0.25,
        -0.25,  0.25,  0.25,
         0.25,  0.25,  0.25,

        -0.25, -0.25,  0.25,
        -0.25, -0.25, -0.25,
        -0.25,  0.25,  0.25,

        -0.25, -0.25, -0.25,
        -0.25,  0.25, -0.25,
        -0.25,  0.25,  0.25,

        -0.25, -0.25,  0.25,
         0.25, -0.25,  0.25,
         0.25, -0.25, -0.25,

         0.25, -0.25, -0.25,
        -0.25, -0.25, -0.25,
        -0.25, -0.25,  0.25,

        -0.25,  0.25, -0.25,
         0.25,  0.25, -0.25,
         0.25,  0.25,  0.25,

         0.25,  0.25,  0.25,
        -0.25,  0.25,  0.25,
        -0.25,  0.25, -0.25 ]

    buffer = GLuint(0)
    glGenBuffers(1, buffer);
    glBindBuffer(GL_ARRAY_BUFFER, buffer);

    #ar=numpy.array(vertex_positions, dtype='float32')
    ar=array("f",vertex_positions)
    glBufferData(GL_ARRAY_BUFFER, ar.tostring(), GL_STATIC_DRAW)

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None);
    glEnableVertexAttribArray(0);

    glEnable(GL_CULL_FACE);
    glFrontFace(GL_CW);

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);

    return program


class Scene:

    def __init__(self, width, height):

        self.width = width
        self.height = height

    def display(self):
        global mv_location
        global proj_location
        global proj_matrix
        global many_cubes

        currentTime = time.time()

        green = [ 0.0, 0.25, 0.0, 1.0 ]
        one = 1.0;

        glViewport(0, 0, int((1360/2)-(512/2)), int((768/2)-(512/2)))


        glClearBufferfv(GL_COLOR, 0, green);
        glClearBufferfv(GL_DEPTH, 0, one);        

        glUseProgram(compile_program(vs_source, fs_source))

        #proj_matrix = m3dOrtho(-1, 1, -1, 1, -10, 10)
        #proj_matrix = m3dPerspective(50.0*math.pi/180.0, 512/512, 0.1, 1000.0)
        #proj_matrix = m3dPerspective(m3dDegToRad(50.0), float(self.width) / float(self.height), 0.1, 1000.0);

        glUniformMatrix4fv(proj_location, 1, GL_FALSE, proj_matrix)

        if (many_cubes == True):

            for i in range(0, 24):
                f = i + currentTime * 0.3;

                mv_matrix = (GLfloat * 16)(*identityMatrix)

                T = (GLfloat * 16)(*identityMatrix)
                m3dTranslateMatrix44(T, 0, 0, -4)

                W = (GLfloat * 16)(*identityMatrix)
                m3dTranslateMatrix44(W, sin(2.1 * f) * 0.5, cos(1.7 * f) * 0.5, sin(1.3 * f) * cos(1.5 * f) * 2.0)

                RX = (GLfloat * 16)(*identityMatrix)
                m3dRotationMatrix44(RX, currentTime * m3dDegToRad(45.0), 0.0, 1.0, 0.0)

                RY = (GLfloat * 16)(*identityMatrix)
                m3dRotationMatrix44(RY, currentTime * m3dDegToRad(81.0), 1.0, 0.0, 0.0)


                mv_matrix = m3dMultiply(W, m3dMultiply(T, m3dMultiply(RY, RX)))

                # or can multiply with numpy
                #R = np.matmul(np.array(W).reshape(4,4) , np.matmul(np.array(RX).reshape(4,4), np.array(RY).reshape(4,4)))
                #mv_matrix = np.matmul(R, np.array(T).reshape(4,4))


                # third way this could be done
                # T  = np.matrix(translate(0.0, 0.0, -4.0)).reshape(4,4)
                # W  = np.matrix(translate(sin(2.1 * f) * 0.5, cos(1.7 * f) * 0.5, sin(1.3 * f) * cos(1.5 * f) * 2.0)).reshape(4,4)
                # RX = np.matrix(rotation_matrix( [1.0, 0.0, 0.0], currentTime * m3dDegToRad(17.0)))
                # RY = np.matrix(rotation_matrix( [0.0, 1.0, 0.0], currentTime * m3dDegToRad(13.0)))
                # mv_matrix = RX * RY * T * W

                glUniformMatrix4fv(mv_location, 1, GL_FALSE, mv_matrix)

                glDrawArrays(GL_TRIANGLES, 0, 36)

        else:
            f = currentTime * 0.3;

            mv_matrix = (GLfloat * 16)(*identityMatrix)

            T = (GLfloat * 16)(*identityMatrix)
            m3dTranslateMatrix44(T, 0, 0, -4)

            W = (GLfloat * 16)(*identityMatrix)
            m3dTranslateMatrix44(W, sin(2.1 * f) * 0.5, cos(1.7 * f) * 0.5, sin(1.3 * f) * cos(1.5 * f) * 2.0)

            RX = (GLfloat * 16)(*identityMatrix)
            m3dRotationMatrix44(RX, currentTime * m3dDegToRad(45.0), 0.0, 1.0, 0.0)

            RY = (GLfloat * 16)(*identityMatrix)
            m3dRotationMatrix44(RY, currentTime * m3dDegToRad(81.0), 1.0, 0.0, 0.0)

            mv_matrix = m3dMultiply(W, m3dMultiply(T, m3dMultiply(RY, RX)))

            # second way to that can multiply with numpy
            #R = np.matmul(np.array(W).reshape(4,4) , np.matmul(np.array(RX).reshape(4,4), np.array(RY).reshape(4,4)))
            #mv_matrix = np.matmul(R, np.array(T).reshape(4,4))


            # third way this could be done
            # T  = np.matrix(translate(0.0, 0.0, -4.0)).reshape(4,4)
            # W  = np.matrix(translate(sin(2.1 * f) * 0.5, cos(1.7 * f) * 0.5, sin(1.3 * f) * cos(1.5 * f) * 2.0)).reshape(4,4)
            # RX = np.matrix(rotation_matrix( [1.0, 0.0, 0.0], currentTime * m3dDegToRad(17.0)))
            # RY = np.matrix(rotation_matrix( [0.0, 1.0, 0.0], currentTime * m3dDegToRad(13.0)))
            # mv_matrix = RX * RY * T * W


            glUniformMatrix4fv(mv_location, 1, GL_FALSE, mv_matrix)

            glDrawArrays(GL_TRIANGLES, 0, 36)

        glutSwapBuffers()

    def reshape(self, width, height):
        global proj_matrix
        proj_matrix = m3dPerspective(m3dDegToRad(50.0), float(self.width) / float(self.height), 0.1, 1000.0);

        self.width = width
        self.height = height

    def keyboard(self, key, x, y ):
        global fullscreen
        global many_cubes

        print ('key:' , key)
        if key == b'\x1b': # ESC
            sys.exit()

        elif key == b'f' or key == b'F': #fullscreen toggle

            if (fullscreen == True):
                glutReshapeWindow(512, 512)
                glutPositionWindow(int((1360/2)-(512/2)), int((768/2)-(512/2)))
                fullscreen = False
            else:
                glutFullScreen()
                fullscreen = True

        elif key == b'm' or key == b'M': 

            if (many_cubes == True):
                many_cubes = False
            else:
                many_cubes = True

        print('done')

    def init(self):
        pass

    def timer(self, blah):

        glutPostRedisplay()
        glutTimerFunc( int(1/60), self.timer, 0)
        time.sleep(1/60.0)


if __name__ == '__main__':
    start = time.time()

    glutInit()
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)

    glutInitWindowSize(512, 512)

    w1 = glutCreateWindow('OpenGL SuperBible - Spinny Cube')
    glutInitWindowPosition(int((1360/2)-(512/2)), int((768/2)-(512/2)))

    fullscreen = False
    many_cubes = False
    #glutFullScreen()

    scene = Scene(512,512)
    glutReshapeFunc(scene.reshape)
    glutDisplayFunc(scene.display)
    glutKeyboardFunc(scene.keyboard)

    glutIdleFunc(scene.display)
    #glutTimerFunc( int(1/60), scene.timer, 0)

    scene.init()

    glutMainLoop()

1 个答案:

答案 0 :(得分:1)

问题的表达形式:

mv_matrix = np.array(A * B * C * D)

numpy.array的元素进行按组件的乘法。

numpy.matmul可以执行矩阵的串联。

操作

C = A * B

可以表示为

C = np.matmul(B, A)

因此,连接4个矩阵A * B * C * D是:

mv_matrix = np.matmul(D, np.matmul(C, np.matmul(B, A)))

请注意,如果您使用numpy.matrix而不是numpy.array,则*运算符将进行矩阵乘法。


旁注:身份矩阵可以由numpy.identity

设置
ident4x4 = np.identity(4, np.float32)

由于输出的数据类型默认为float,因此可以进一步简化:

ident4x4 = np.identity(4)

例如使用函数translaterotation_matrix来连接围绕x和y轴的平移和旋转:

T  = np.matrix(translate(0.0, 0.0, -4.0)).reshape(4,4)
RX = np.matrix(rotation_matrix( [1.0, 0.0, 0.0], currentTime * m3dDegToRad(17.0)))
RY = np.matrix(rotation_matrix( [0.0, 1.0, 0.0], currentTime * m3dDegToRad(13.0)))

mv_matrix = RX * RY * T