【问题标题】:Rotating a Cube using Quaternions in PyOpenGL在 PyOpenGL 中使用四元数旋转立方体
【发布时间】:2015-06-10 01:43:47
【问题描述】:

我最近对图形编程产生了兴趣,并尝试从使用 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')

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

我使用的逻辑也是基于另一个堆栈溢出问题 (opengl matrix rotation quaternions)。我和问这个问题的人有几乎完全相同的问题。但是,我不太明白他的解决方案与他最初使用四元数的尝试有何不同。这可能是由于不精通 C/C++。

【问题讨论】:

    标签: python opengl transformation quaternions pyopengl


    【解决方案1】:

    事实证明,导致旋转无法正常工作的主要错误有两个:旋转的累积和使用的矩阵模式。

    之前累积旋转的方式是通过计算当前帧的四元数,将其转换为矩阵,然后使用 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()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-06
      • 1970-01-01
      • 1970-01-01
      • 2018-02-23
      • 2013-07-17
      • 2019-11-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多