在PyOpenGL中使用四元数旋转多维数据集

我最近对图形编程感兴趣,并试图通过使用PyOpenGL和PyGame制作一个简单的多维数据集来开始。 我已经设法让它旋转,但我无法正确连续旋转。

我在空间中有一个简单的立方体,我想使用箭头键(最终使用鼠标)在全局x和y轴上旋转它。 然而,我试图应用于立方体的所有旋转都会在局部x和y轴上相对于立方体的方向旋转它,而不是关于观察者的视角的全局坐标轴。

我尝试的第一件事是使用glRotatef,但当然导致了局部旋转。 环顾四周后,我发现很多人都有类似的问题,除了“尝试这个”或“使用四元数”之外,没有多少答案提供了很多信息,所以我尝试了四元数。 我现在明白(大部分)四元数是如何工作的,但由于某种原因,我使用它们仍然导致同样的问题,所以我认为我可能在某个地方错过了一两步。

以下是我正在使用的代码:

import numpy
from math import *
from quat import *

import pygame
from pygame.locals import *

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

axis_points = (
    (-6.0, 0.0, 0.0),
    ( 6.0, 0.0, 0.0),
    ( 0.0,-6.0, 0.0),
    ( 0.0, 6.0, 0.0),
    ( 0.0, 0.0,-6.0),
    ( 0.0, 0.0, 6.0)
    )

axes = (
    (0,1),
    (2,3),
    (4,5)
    )


verticies = (
    (-3.0,-3.0, 3.0),
    (-3.0, 3.0, 3.0),
    ( 3.0, 3.0, 3.0),
    ( 3.0,-3.0, 3.0),
    (-3.0,-3.0,-3.0),
    (-3.0, 3.0,-3.0),
    ( 3.0, 3.0,-3.0),
    ( 3.0,-3.0,-3.0),
    )

edges = (
    (0,1),
    (0,3),
    (0,4),
    (2,1),
    (2,3),
    (2,6),
    (5,1),
    (5,4),
    (5,6),
    (7,3),
    (7,4),
    (7,6)
    )

surfaces = (
    (0,1,2,3),
    (3,2,6,7),
    (7,6,5,4),
    (4,5,1,0),
    (1,5,6,2),
    (4,0,3,7),
    )

colors = (
    (0.769,0.118,0.227), # Red
    (  0.0,0.318,0.729), # Blue
    (  1.0,0.345,  0.0), # Orange
    (  0.0, 0.62,0.376), # Green
    (  1.0,  1.0,  1.0), # White
    (  1.0,0.835,  0.0), # Yellow
    )


def Axis():
    glBegin(GL_LINES)
    glColor3f(1,1,1)
    for axis in axes:
        for point in axis:
            glVertex3fv(axis_points[point])
    glEnd()

def Cube():
    glBegin(GL_QUADS)
    for color,surface in zip(colors,surfaces):
        glColor3fv(color)
        for vertex in surface:
            glVertex3fv(verticies[vertex])
    glEnd()

    glBegin(GL_LINES)
    glColor3fv((0,0,0))
    for edge in edges:
        for vertex in edge:
            glVertex3fv(verticies[vertex])
    glEnd()

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

    # Using depth test to make sure closer colors are shown over further ones
    glEnable(GL_DEPTH_TEST)
    glDepthFunc(GL_LESS)

    # Default view
    gluPerspective(45, (display[0]/display[1]), 0.05, 50.0)
    glTranslatef(0.0,0.0,-25)


    inc_x = 0
    inc_y = 0

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

            if event.type == pygame.KEYDOWN:
                print "Modelview Matrix:"
                print glGetFloatv(GL_MODELVIEW_MATRIX)

                #Rotating about the x axis
                if event.key == pygame.K_UP:
                    inc_x =  pi/100
                if event.key == pygame.K_DOWN:
                    inc_x = -pi/100

                # Rotating about the y axis
                if event.key == pygame.K_LEFT:
                    inc_y =  pi/100
                if event.key == pygame.K_RIGHT:
                    inc_y = -pi/100

                # Reset to default view
                if event.key == pygame.K_SPACE:
                    glLoadIdentity()
                    gluPerspective(45, (display[0]/display[1]), 0.05, 50.0)
                    glTranslatef(0.0,0.0,-25)

            if event.type == pygame.KEYUP:
                # Stoping rotation
                if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
                    inc_x = 0.0
                if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
                    inc_y = 0.0

        rot_x = normalize(axisangle_to_q((1.0,0.0,0.0), inc_x))
        rot_y = normalize(axisangle_to_q((0.0,1.0,0.0), inc_y))

        tot_rot = q_to_mat4(q_mult(rot_x,rot_y))
        glMultMatrixf(tot_rot)


        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
        Cube()
        Axis()
        pygame.display.flip()
        pygame.time.wait(10)

main()

from math import *
import numpy

def normalize(v, tolerance=0.00001):
    mag2 = sum(n * n for n in v)
    if abs(mag2 - 1.0) > tolerance:
        mag = sqrt(mag2)
        v = tuple(n / mag for n in v)
    return v

def q_mult(q1, q2):
    w1, x1, y1, z1 = q1
    w2, x2, y2, z2 = q2
    w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2
    x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2
    y = w1 * y2 + y1 * w2 + z1 * x2 - x1 * z2
    z = w1 * z2 + z1 * w2 + x1 * y2 - y1 * x2
    return w, x, y, z

def q_conjugate(q):
    w, x, y, z = q
    return (w, -x, -y, -z)

def qv_mult(q1, v1):
    q2 = (0.0,) + v1
    return q_mult(q_mult(q1, q2), q_conjugate(q1))[1:]

def axisangle_to_q(v, theta):
    v = normalize(v)
    x, y, z = v
    theta /= 2
    w = cos(theta)
    x = x * sin(theta)
    y = y * sin(theta)
    z = z * sin(theta)
    return w, x, y, z

def q_to_axisangle(q):
    w, v = q[0], q[1:]
    theta = acos(w) * 2.0
    return normalize(v), theta

def q_to_mat4(q):
    w, x, y, z = q
    return numpy.array(
        [[1 - 2*y*y - 2*z*z, 2*x*y - 2*z*w, 2*x*z + 2*y*w, 0],
        [2*x*y + 2*z*w, 1 - 2*x*x - 2*z*z, 2*y*z - 2*x*w, 0],
        [2*x*z - 2*y*w, 2*y*z + 2*x*w, 1 - 2*x*x - 2*y*y, 0],
        [0, 0, 0, 1] ],'f')

我找到了quaterion方法的代码,在某人回答关于在python中实现四元数的堆栈溢出问题,但我找不到指向它的链接。

我使用的逻辑基于另一个堆栈溢出问题(opengl矩阵旋转四元数)。 我和问这个问题的人有几乎完全相同的问题。 不过,我不太明白他的解决方案与他最初尝试使用四元数的方法不同。 这可能是由于不熟悉C / C ++。


所以原来有两个主要的错误导致了旋转不正确的工作:旋转的积累和矩阵模式的使用。

之前积累的旋转方式是通过计算当前帧的四元数,将其转换为矩阵,然后使用glMultMatrixf应用并“累加”旋转。 现在正在完成的方式是在主循环之前设置一个四元数累加器,并将每次旋转与累加器相乘。 然后将累加器转换为矩阵,并使用glLoadMatrix将当前矩阵设置为累计旋转。 虽然我并不是100%确定为什么这解决了我的问题,但我最好的猜测是,它与glMultMatrix乘以变换的顺序相比只是跟踪四元数中的整体旋转。 我也不完全相信这是最有效的解决方案。

我在旋转工作后遇到的第二个问题是我的立方体被扭曲成一个平截体。 这是由于在设置gluPerspective和加载旋转矩阵时使用了不正确的矩阵模式。

以下是我可以参考的工作代码。

import numpy
from math import *
from quat import *

import pygame
from pygame.locals import *

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


axis_verts = (
    (-7.5, 0.0, 0.0),
    ( 7.5, 0.0, 0.0),
    ( 0.0,-7.5, 0.0),
    ( 0.0, 7.5, 0.0),
    ( 0.0, 0.0,-7.5),
    ( 0.0, 0.0, 7.5)
    )

axes = (
    (0,1),
    (2,3),
    (4,5)
    )

axis_colors = (
    (1.0,0.0,0.0), # Red
    (0.0,1.0,0.0), # Green
    (0.0,0.0,1.0)  # Blue
    )


'''
       5____________6
       /           /|
      /           / |
    1/__________2/  |
    |           |   |
    |           |   |
    |           |   7
    |           |  /
    |           | /
    0___________3/
'''

cube_verts = (
    (-3.0,-3.0, 3.0),
    (-3.0, 3.0, 3.0),
    ( 3.0, 3.0, 3.0),
    ( 3.0,-3.0, 3.0),
    (-3.0,-3.0,-3.0),
    (-3.0, 3.0,-3.0),
    ( 3.0, 3.0,-3.0),
    ( 3.0,-3.0,-3.0)
    )

cube_edges = (
    (0,1),
    (0,3),
    (0,4),
    (2,1),
    (2,3),
    (2,6),
    (5,1),
    (5,4),
    (5,6),
    (7,3),
    (7,4),
    (7,6)
    )

cube_surfaces = (
    (0,1,2,3), # Front
    (3,2,6,7), # Right
    (7,6,5,4), # Left
    (4,5,1,0), # Back
    (1,5,6,2), # Top
    (4,0,3,7)  # Bottom
    )

cube_colors = (
    (0.769,0.118,0.227), # Red
    (  0.0,0.318,0.729), # Blue
    (  1.0,0.345,  0.0), # Orange
    (  0.0, 0.62,0.376), # Green
    (  1.0,  1.0,  1.0), # White
    (  1.0,0.835,  0.0)  # Yellow
    )


def Axis():
    glBegin(GL_LINES)
    for color,axis in zip(axis_colors,axes):
        glColor3fv(color)
        for point in axis:
            glVertex3fv(axis_verts[point])
    glEnd()

def Cube():
    glBegin(GL_QUADS)
    for color,surface in zip(cube_colors,cube_surfaces):
        glColor3fv(color)
        for vertex in surface:
            glVertex3fv(cube_verts[vertex])
    glEnd()

    glBegin(GL_LINES)
    glColor3fv((0.0,0.0,0.0))
    for edge in cube_edges:
        for vertex in edge:
            glVertex3fv(cube_verts[vertex])
    glEnd()

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

    # Using depth test to make sure closer colors are shown over further ones
    glEnable(GL_DEPTH_TEST)
    glDepthFunc(GL_LESS)

    # Default view
    glMatrixMode(GL_PROJECTION)
    gluPerspective(45, (display[0]/display[1]), 0.5, 40)
    glTranslatef(0.0,0.0,-17.5)


    inc_x = 0
    inc_y = 0
    accum = (1,0,0,0)

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

            if event.type == pygame.KEYDOWN:
                #Rotating about the x axis
                if event.key == pygame.K_UP:
                    inc_x =  pi/100
                if event.key == pygame.K_DOWN:
                    inc_x = -pi/100

                # Rotating about the y axis
                if event.key == pygame.K_LEFT:
                    inc_y =  pi/100
                if event.key == pygame.K_RIGHT:
                    inc_y = -pi/100

                # Reset to default view
                if event.key == pygame.K_SPACE:
                    accum = (1,0,0,0)

            if event.type == pygame.KEYUP:
                # Stoping rotation
                if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
                    inc_x = 0.0
                if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
                    inc_y = 0.0

        rot_x = normalize(axisangle_to_q((1.0,0.0,0.0), inc_x))
        rot_y = normalize(axisangle_to_q((0.0,1.0,0.0), inc_y))
        accum = q_mult(accum,rot_x)
        accum = q_mult(accum,rot_y)

        glMatrixMode(GL_MODELVIEW)
        glLoadMatrixf(q_to_mat4(accum))

        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
        Cube()
        Axis()
        pygame.display.flip()
        pygame.time.wait(10)

main()
链接地址: http://www.djcxy.com/p/14355.html

上一篇: Rotating a Cube using Quaternions in PyOpenGL

下一篇: opengl rotate an object around fixed axes