转向时,OpenGL Rubik的多维数据集颜色无法正确显示

时间:2019-06-24 03:06:29

标签: python numpy opengl 3d pyopengl

我一直在尝试使用opengl编写3d rubiks多维数据集,但是像许多其他示例一样,在理解每个具有自己的矩阵并使用glMultMatrixf()的概念时遇到了麻烦。相反,当我旋转一个立方体的侧面时,我一直在切换每个单独的立方体的颜色。但是,当我向右旋转然后向上旋转颜色时,有时会引起问题。我想知道是否有可能通过切换颜色来做到这一点,是否可以帮助我调试问题。谢谢!

例如,现在,如果我先执行turn_x然后执行turn_y,则在我切换多维数据集上的颜色时,颜色将无法正确显示。有没有办法更改lr和ud功能,以便可以正确切换颜色?任何帮助将不胜感激!

主要:

dim = 3
cube = []

for x in range(-1, 2):
    for y in range(-1, 2):
        for z in range(-1, 2):
            matrix = np.matrix([[x, 0, 0], [0, y, 0], [0, 0, z]])
            cube.append(Piece(x, y, z, matrix))

def turn_x(direction):
    trans_matrix = np.matrix([[0, 1], [-1, 0]])
    for i in range(len(cube)):
        block = cube[i]
        if block.x == direction:
            new_point = trans_matrix * np.matrix([[block.y], [block.z]])
            block.update(block.x, new_point.item(0), new_point.item(1))
            block.lr(direction)


def turn_y(direction):
    trans_matrix = np.matrix([[0, 1], [-1, 0]])
    for i in range(len(cube)):
        block = cube[i]
        if block.y == direction:
            new_point = trans_matrix * np.matrix([[block.x], [block.z]])
            block.update(new_point.item(0), block.y, new_point.item(1))
            block.ud(direction)


def main():
    pygame.init()
    display = (1200, 1000)
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)

    gluPerspective(45, (display[0] / display[1]), 0.1, 50)

    glClearColor(0.6, 0.6, 0.6, 0)
    glTranslatef(0.0, 0.0, -20)
    glRotatef(45, 1, 1, 0)

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_3:
                    turn_x(1)
                if event.key == pygame.K_5:
                    turn_y(1)

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        for c in cube:
            c.draw()
        pygame.display.flip()
        pygame.time.wait(10)
main()

件:

class Piece:

    def __init__(self, x, y, z, position):
        self.x = x
        self.y = y
        self.z = z
        self.len = 0.5
        self.v = [
            (self.x - self.len, self.y - self.len, self.z - self.len),
            (self.x + self.len, self.y - self.len, self.z - self.len),
            (self.x + self.len, self.y + self.len, self.z - self.len),
            (self.x - self.len, self.y + self.len, self.z - self.len),
            (self.x - self.len, self.y - self.len, self.z + self.len),
            (self.x + self.len, self.y - self.len, self.z + self.len),
            (self.x + self.len, self.y + self.len, self.z + self.len),
            (self.x - self.len, self.y + self.len, self.z + self.len),
        ]
        self.edges = [(0, 1), (1, 2), (2, 3), (3, 0), (4, 5), (5, 6),
                      (6, 7), (7, 4), (0, 4), (1, 5), (2, 6), (3, 7)]
        self.surfaces = [(0, 1, 2, 3), (5, 4, 7, 6), (4, 0, 3, 7), (1, 5, 6, 2),
                         (4, 5, 1, 0), (3, 2, 6, 7)]
        self.colors = [ # blue
                       (0, 0, 1),
                        # green
                       (0, 1, 0),
                        # orange
                       (1, 0.5, 0.1),
                        # red
                       (1, 0, 0),
                        # yellow
                       (1, 1, 0),
                        # white
                       (1, 1, 1)]
        self.matrix = position

    def lr(self, dir):
        color_ind = []
        if dir == -1:
            color_ind = [4, 5, 2, 3, 1, 0]
        else:
            color_ind = [5, 4, 2, 3, 0, 1]
        new_colors = []
        for i in color_ind:
            new_colors.append(self.colors[i])
        self.colors = new_colors

    def ud(self, dir):
        color_ind = []
        if dir == 1:
            color_ind = [2, 3, 1, 0, 4, 5]
        else:
            color_ind = [3, 2, 0, 1, 4, 5]
        new_colors = []
        for i in color_ind:
            new_colors.append(self.colors[i])
        self.colors = new_colors

    def update(self, x, y, z):
        self.matrix = np.matrix([[x, 0, 0], [0, y, 0], [0, 0, z]])
        print(self.matrix)
        self.x = x
        self.y = y
        self.z = z
        self.update_vertices()

    def update_vertices(self):
        self.v = [
            (self.x - self.len, self.y - self.len, self.z - self.len),
            (self.x + self.len, self.y - self.len, self.z - self.len),
            (self.x + self.len, self.y + self.len, self.z - self.len),
            (self.x - self.len, self.y + self.len, self.z - self.len),
            (self.x - self.len, self.y - self.len, self.z + self.len),
            (self.x + self.len, self.y - self.len, self.z + self.len),
            (self.x + self.len, self.y + self.len, self.z + self.len),
            (self.x - self.len, self.y + self.len, self.z + self.len),
        ]

    def draw(self):
        glEnable(GL_DEPTH_TEST)

        glEnable(GL_POLYGON_OFFSET_FILL)
        glPolygonOffset(1.0, 1.0)

        glLineWidth(5)
        glColor3fv((0, 0, 0))
        glBegin(GL_LINES)
        for e in self.edges:
            glVertex3fv(self.v[e[0]])
            glVertex3fv(self.v[e[1]])
        glEnd()

        glBegin(GL_QUADS)
        for i in range(len(self.surfaces)):
            glColor3fv(self.colors[i])
            for j in self.surfaces[i]:
                glVertex3fv(self.v[j])
        glEnd()

1 个答案:

答案 0 :(得分:0)

不需要矩阵级联,因为您要做的就是将向量向左或向右旋转90°。

可以通过交换x和y分量来实现(二维)矢量90度旋转。为了向(数学)右旋转,必须将结果的x分量反转。要向左旋转,必须将结果的y分量反转:

v       = (x, y)
v_right = (-y, x)
v_left  = (y, -x)

在函数turn_x()turn_y()中利用它:

def turn_x(layer, direction):
    for i in range(len(cube)):
        block = cube[i]
        if block.x == layer:
            block.update(block.x, block.z*direction, -block.y*direction)
            block.lr(direction)

def turn_y(layer, direction):
    for i in range(len(cube)):
        block = cube[i]
        if block.y == layer:
            block.update(block.z*direction, block.y, -block.x*direction)
            block.ud(direction)

如果要使用numpy.matrix进行相同操作,则必须将2x2矩阵乘以2x1矩阵:

def turn_x(layer, direction):
    trans_matrix = np.matrix([[0, 1*direction], [-1*direction, 0]])
    for i in range(len(cube)):
        block = cube[i]
        if block.x == layer:
            new_point = trans_matrix * np.matrix([[block.y], [block.z]])
            block.update(block.x, new_point.item(0), new_point.item(1))
            block.lr(direction)

def turn_y(layer, direction):
    trans_matrix = np.matrix([[0, 1*direction], [-1*direction, 0]])
    for i in range(len(cube)):
        block = cube[i]
        if block.y == layer:
            new_point = trans_matrix * np.matrix([[block.x], [block.z]])
            block.update(new_point.item(0), block.y, new_point.item(1))
            block.ud(direction)

此外,您还必须确保函数turn_x()和方法Piece.lr()分别turn_y()turn_y.ud()沿相同方向旋转。

如果dir==1,则.lr().ud()应该向左旋转。在.lr()中就是这种情况,但是在.ud()中是错误的:

def ud(self, dir):
    color_ind = []
    if dir == -1:   # <------------------ == -1 instead of == 1
        color_ind = [2, 3, 1, 0, 4, 5]
    else:
        color_ind = [3, 2, 0, 1, 4, 5]

使用以下主程序。可以使用 << / kbd>,> ^ v 键旋转场景,以研究旋转。 br /> 3 向上旋转左外层,向下旋转 4
5 将上一层向右旋转, 6 向左旋转。

def main():
    pygame.init()
    display = (400, 400)
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)

    glMatrixMode(GL_PROJECTION)
    gluPerspective(45, (display[0] / display[1]), 0.1, 50)

    angle_x = 0
    angle_y = 0
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_3:
                    turn_x(1, 1)
                if event.key == pygame.K_4:
                    turn_x(1, -1)
                if event.key == pygame.K_5:
                    turn_y(1, 1)
                if event.key == pygame.K_6:
                    turn_y(1, -1)
        keys = pygame.key.get_pressed()
        if keys[pygame.K_UP]:
            angle_y += 5
        if keys[pygame.K_DOWN]:
            angle_y -= 5
        if keys[pygame.K_RIGHT]:
            angle_x += 5
        if keys[pygame.K_LEFT]:
            angle_x -= 5

        glClearColor(0.6, 0.6, 0.6, 0)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        glTranslatef(0.0, 0.0, -10)
        glRotatef(45, 1, 1, 0)

        glRotatef(angle_y, -1, 0, 0)
        glRotatef(angle_x, 0, 1, 0)

        for c in cube:
            c.draw()
        pygame.display.flip()
        pygame.time.wait(10)

main()

无论如何,我建议不要交换立方体的面,也不要更改立方体的顶点。
创建一个最小值为(-1,-1,-1)且最大值为(1、1、1)的多维数据集。
多维数据集的位置由属性(self.xself.yself.z)定义,其比例由self.len定义。
添加一个3x3旋转矩阵属性,该属性定义当前旋转。旋转矩阵由[[1, 0, 0], [0, 1, 0], [0, 0, 1]]初始化:

class Piece:

    def __init__(self, x, y, z):
        self.x, self.y, self.z = x, y, z
        self.len = 0.5
        self.rotation = np.matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]])

        self.v = [(-1,-1,-1), (1,-1,-1), (1,1,-1), (-1,1,-1), (-1,-1,1), (1,-1,1), (1,1,1), (-1,1,1)]
        self.edges = [(0, 1), (1, 2), (2, 3), (3, 0), (4, 5), (5, 6),
                      (6, 7), (7, 4), (0, 4), (1, 5), (2, 6), (3, 7)]
        self.surfaces = [(0, 1, 2, 3), (5, 4, 7, 6), (4, 0, 3, 7), (1, 5, 6, 2),
                         (4, 5, 1, 0), (3, 2, 6, 7)]
        self.colors = [ 
            (0, 0, 1),     # blue
            (0, 1, 0),     # green
            (1, 0.5, 0.1), # orange
            (1, 0, 0),     # red
            (1, 1, 0),     # yellow
            (1, 1, 1)]     # white

绘制多维数据集后,请使用glScale将多维数据集缩放至其大小,然后使用glTranslate将多维数据集设置为其初始位置。
最终,将缩放和平移的多维数据集旋转到其位置,就好像它是一个“真实”的魔方。为此,必须将3x3矩阵转换为4x4矩阵,并将其乘以glMultMatrix

class Piece:

    # [...]

    def draw(self):

        r = np.array(self.rotation).reshape(9)
        rm44 = [r[0],r[1],r[2],0, r[3],r[4],r[5],0, r[6],r[7],r[8],0, 0,0,0,1]

        glEnable(GL_DEPTH_TEST)

        glEnable(GL_POLYGON_OFFSET_FILL)
        glPolygonOffset(1.0, 1.0)

        glPushMatrix()
        glMultMatrixf(rm44)
        glTranslatef(self.x, self.y, self.z)
        glScalef(self.len, self.len, self.len)

        glLineWidth(5)
        glColor3fv((0, 0, 0))
        glBegin(GL_LINES)
        for e in self.edges:
            glVertex3fv(self.v[e[0]])
            glVertex3fv(self.v[e[1]])
        glEnd()

        glBegin(GL_QUADS)
        for i in range(len(self.surfaces)):
            glColor3fv(self.colors[i])
            for j in self.surfaces[i]:
                glVertex3fv(self.v[j])
        glEnd()

        glPopMatrix()

创建3个方法,这些方法可以使多维数据集绕3轴旋转:

class Piece:

    # [...]

    def rotX(self, dir):
        rot_m = np.matrix([[1, 0, 0], [0, 0, dir], [0, -dir, 0]])
        self.update(rot_m)

    def rotY(self, dir):
        rot_m = np.matrix([[0, 0, -dir], [0, 1, 0], [dir, 0, 0]])
        self.update(rot_m)

    def rotZ(self, dir):
        rot_m = np.matrix([[0, dir, 0], [-dir, 0, 0], [0, 0, 1]])
        self.update(rot_m)

    def update(self, rot_mat):
        self.rotation = self.rotation * rot_mat

最后,您需要一个方法,该方法返回多维数据集的当前位置。旋转函数(turn_xturn_y)需要此方法来标识受影响的多维数据集。此方法通过当前旋转(self.x)旋转立方体的位置矢量(self.yself.zself.rotation),并返回结果:

class Piece:

    # [...]

    def location(self):
        current_pos = np.matrix([self.x, self.y, self.z]) * self.rotation
        return (current_pos.item(0), current_pos.item(1), current_pos.item(2))

构建多维数据集时,不需要矩阵:

cube = [Piece(x, y, z) for x in range(-1, 2) for y in range(-1, 2) for z in range(-1, 2)]

旋转操作只需要使用direction参数分别调用方法.rotX.rotY .rotZ。方向必须为1或-1:

def turn_x(layer, direction):
    [c.rotX(direction) for c in cube if c.location()[0] == layer]

def turn_y(layer, direction):
    [c.rotY(direction) for c in cube if c.location()[1] == layer]

def turn_z(layer, direction):
    [c.rotZ(direction) for c in cube if c.location()[2] == layer]