简体   繁体   English

如何使用OpenGL在3D空间中进行垂直旋转?

[英]how to get vertical rotation in 3D space with OpenGL?

I have a field of cubes created in OpenGL and I have walking around working as expected, and part of rotating the "camera" is working until I try to look up or down. 我有一个在OpenGL中创建的多维数据集领域,我已经按预期工作,并且旋转“相机”的一部分正在工作,直到我尝试向上或向下查看。

I have a snippet of code that kind of works: 我有一段有用的代码片段:

if pressed[pygame.K_UP] or pressed[pygame.K_DOWN]:
            rotx = cos(rot/radian)
            rotz = sin(rot/radian)
            if pressed[pygame.K_UP]:
                glRotatef(speed / 2, -rotx, 0, rotz)
            if pressed[pygame.K_DOWN]:
                glRotatef(speed / 2, rotx, 0, -rotz)

but it only works when rot is 0. so when I first run the program I can look up and down if I only move side to side, and dont look left or right, or move forwards and backwards. 但它只有在腐烂为0时才有效。所以当我第一次运行程序时,如果我只是左右移动,不要向左或向右看,或向前和向后移动,我可以向上和向下看。

verticies = (
    (1, -1, -1),
    (1, 1, -1),
    (-1, 1, -1),
    (-1, -1, -1),
    (1, -1, 1),
    (1, 1, 1),
    (-1, -1, 1),
    (-1, 1, 1)
    )

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


def Cube(tX, tY, tZ):
    glBegin(GL_LINES)
    for edge in edges:
        for vertex in edge:
            glVertex3f(verticies[vertex][0] + tX, verticies[vertex][1] + tY, verticies[vertex][2] + tZ)
    glEnd()


def main():
    pygame.init()
    screenSize = (1500, 800)
    pygame.display.set_mode(screenSize, DOUBLEBUF|OPENGL)

    gluPerspective(45, (screenSize[0]/screenSize[1]), 0.1, 50.0)

    rot = 0
    speed = 3
    radian = 57.2958
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

        pressed = pygame.key.get_pressed()

        #==# Rotation with arrow keys #==#
        if pressed[pygame.K_LEFT]:
            glRotatef(speed / 2, 0, -1, 0)
            rot += 1
        if pressed[pygame.K_RIGHT]:
            glRotatef(speed / 2, 0, 1, 0)
            rot -= 1
        if pressed[pygame.K_UP] or pressed[pygame.K_DOWN]:
            rotx = cos(rot/radian)
            rotz = sin(rot/radian)
            if pressed[pygame.K_UP]:
                glRotatef(speed / 2, -rotx, 0, rotz)
            if pressed[pygame.K_DOWN]:
                glRotatef(speed / 2, rotx, 0, -rotz)

        #==# Walking with WASD #==#
        if pressed[pygame.K_w]:
            glTranslate(sin(rot/radian) / speed, 0, cos(rot/radian) / speed)
        if pressed[pygame.K_s]:
            glTranslate(-sin(rot/radian) / speed, 0, -cos(rot/radian) / speed)

        if pressed[pygame.K_a]:
            glTranslate(sin((rot + 90)/radian) / speed, 0, cos((rot + 90)/radian) / speed)
        if pressed[pygame.K_d]:
            glTranslate(-sin((rot + 90)/radian) / speed, 0, -cos((rot + 90)/radian) / speed)


        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)

        for i in range(8):
            for j in range(8):
                Cube(-i*2.5, -4, -j*2.5)

        pygame.display.flip()
        pygame.time.wait(10)

main()

I thought this would work as the movement and camera in an FPS game, but it doesn't. 我认为这可以作为FPS游戏中的机芯和相机,但事实并非如此。

It's all a matter of the order. 这完全是订单的问题。 OpenGL is a state engine. OpenGL是一个状态引擎。 Each operation changes a state. 每个操作都会改变状态。 When you do a operation like glTranslatef or glRotatef then the current matrix on the matrix stack is changed. 当您执行glTranslatefglRotatef等操作时,矩阵堆栈上的当前矩阵会发生变化。

In OpenGL there are different matrices, like the model view matrix and the projection matrix. 在OpenGL中有不同的矩阵,如模型视图矩阵和投影矩阵。 The first thing you've to do is to separate the projection matrix and model vie matrix. 你要做的第一件事是将投影矩阵和模型与矩阵分开。 THis can be done by setting the matrix mode (See glMatrixMode ): 这可以通过设置矩阵模式来完成(参见glMatrixMode ):

glMatrixMode(GL_PROJECTION)
gluPerspective(45, (screenSize[0]/screenSize[1]), 0.1, 50.0)
glMatrixMode(GL_MODELVIEW)

# [...]

When the a matrix operation is applied to the matrix stack, then the current matrix is multiplied by the new (additional) matrix, which is defined by the operation. 当矩阵运算应用于矩阵堆栈时,则当前矩阵乘以新的(附加)矩阵,该矩阵由操作定义。 This means glRotatef followed by glTranslatef dies: 这意味着glRotatef跟随glTranslatef死:

current_matrix = current_matrix * rotation_matrix * translation_matrix

The issue is that if you want to apply a new translation or rotation in a first person view, the the new transformation has to be applied on the current view (current model view matrix). 问题是,如果要在第一人称视图中应用新的平移或旋转,则必须在当前视图(当前模型视图矩阵)上应用新变换。 This mean the operations have to be performed in the opposite order, than the matrix operation does: 这意味着操作必须以与矩阵操作相反的顺序执行:

current_matrix = rotation_matrix * translation_matrix * current_matrix

You've tried to compensate this, by considering the current direction of view, which you've calculated by trigonometric functions. 您已经尝试通过考虑您通过三角函数计算的当前视图方向来补偿这一点。 But there is alternative solution: 但是有另一种解决方案:

current_mv_mat = (GLfloat * 16)() 
glGetFloatv(GL_MODELVIEW_MATRIX, current_mv_mat)
glLoadIdentity()

# [...] glTranslatef, glRotatef

glMultMatrixf(mv)

Even more trick is the up and down rotation which has to be performed on the current view, but sadly it should not be stated in on in the current matrix, since the movement and respectively left and right rotation should not depend on the up and down view: 更多的技巧是必须在当前视图上执行的向上和向下旋转,但遗憾的是它不应该在当前矩阵中声明,因为运动和左右旋转不应该依赖于上下视图:

current_matrix = rotation_matrix * translation_matrix * current_matrix
mdel_view_matrix = roate_updown * current_matrix

Fortunately the current matrix is manged on a stack and can be pushed and popped by glPushMatrix / glPopMatrix . 幸运的是,当前矩阵在堆栈上被管理,并且可以由glPushMatrix / glPopMatrix推送和弹出。 The up ad down rotation has to be summed up and finally applied to the view: 向上广告向下旋转必须总结并最终应用于视图:

glPushMatrix()

current_mv_mat = (GLfloat * 16)()
glGetFloatv(GL_MODELVIEW_MATRIX, current_mv_mat)
glLoadIdentity()
glRotatef(sum_rot_updown, 1, 0, 0)
glMultMatrixf(mv)

# [...] draw all the objects of the scene

glPopMatrix()

See the example, where I applied the suggestions to your original code: 请参阅示例,我将建议应用于原始代码:

def main():
    pygame.init()
    screenSize = (1500, 800)
    pygame.display.set_mode(screenSize, DOUBLEBUF|OPENGL)

    glMatrixMode(GL_PROJECTION)
    gluPerspective(45, (screenSize[0]/screenSize[1]), 0.1, 50.0)
    glMatrixMode(GL_MODELVIEW)

    rot = 0
    speed = 3
    radian = 57.2958
    sum_rot_updown = 0
    current_mv_mat = (GLfloat * 16)()
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

        pressed = pygame.key.get_pressed()

        glGetFloatv(GL_MODELVIEW_MATRIX, current_mv_mat)
        glLoadIdentity()

        #==# Rotation left and right with arrow keys #==#
        if pressed[pygame.K_LEFT]:
            glRotatef(speed / 2, 0, -1, 0)
            rot += 1
        if pressed[pygame.K_RIGHT]:
            glRotatef(speed / 2, 0, 1, 0)
            rot -= 1

        #==# Walking with WASD #==#
        if pressed[pygame.K_w]:
            glTranslate(0, 0, 1/speed)
        if pressed[pygame.K_s]:
            glTranslate(0, 0, -1/speed)
        if pressed[pygame.K_a]:
            glTranslate(1/speed, 0, 0)
        if pressed[pygame.K_d]:
            glTranslate(-1/speed, 0, 0)

        glMultMatrixf(current_mv_mat)

        #==# Rotation up and down with arrow keys #==# 
        if pressed[pygame.K_UP]:
            sum_rot_updown -= speed / 2
        if pressed[pygame.K_DOWN]:
            sum_rot_updown += speed / 2

        glPushMatrix()

        glGetFloatv(GL_MODELVIEW_MATRIX, current_mv_mat)
        glLoadIdentity()
        glRotatef(sum_rot_updown, 1, 0, 0)
        glMultMatrixf(current_mv_mat)

        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)

        for i in range(8):
            for j in range(8):
                Cube(-i*2.5, -4, -j*2.5)

        glPopMatrix()

        pygame.display.flip()
        pygame.time.wait(10)

main()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM