如何使用 OpenGL 在 3D 空间中获得垂直旋转? [英] how to get vertical rotation in 3D space with OpenGL?

查看:67
本文介绍了如何使用 OpenGL 在 3D 空间中获得垂直旋转?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个在 OpenGL 中创建的立方体字段,我按预期工作,并且旋转相机"的一部分正在工作,直到我尝试向上或向下查看.

我有一段有效的代码:

如果按下[pygame.K_UP]或按下[pygame.K_DOWN]:rotx = cos(rot/弧度)rotz = sin(rot/弧度)如果按下[pygame.K_UP]:glRotatef(速度/2,-rotx,0,rotz)如果按下[pygame.K_DOWN]:glRotatef(速度/2, rotx, 0, -rotz)

但它只在 rot 为 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))边 = ((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)对于边缘中的边缘:对于边缘中的顶点:glVertex3f(顶点[顶点][0] + tX,顶点[顶点][1] + tY,顶点[顶点][2] + tZ)glEnd()定义主():pygame.init()屏幕尺寸 = (1500, 800)pygame.display.set_mode(screenSize, DOUBLEBUF|OPENGL)gluPerspective(45, (screenSize[0]/screenSize[1]), 0.1, 50.0)腐烂 = 0速度 = 3弧度 = 57.2958为真:对于 pygame.event.get() 中的事件:如果 event.type == pygame.QUIT:pygame.quit()退出()按下 = pygame.key.get_pressed()#==#用箭头键旋转#==#如果按下[pygame.K_LEFT]:glRotatef(速度/2, 0, -1, 0)腐烂 += 1如果按下[pygame.K_RIGHT]:glRotatef(速度/2, 0, 1, 0)腐烂 -= 1如果按下[pygame.K_UP]或按下[pygame.K_DOWN]:rotx = cos(rot/弧度)rotz = sin(rot/弧度)如果按下[pygame.K_UP]:glRotatef(速度/2,-rotx,0,rotz)如果按下[pygame.K_DOWN]:glRotatef(速度/2, rotx, 0, -rotz)#==# 与 WASD 同行#==#如果按下[pygame.K_w]:glTranslate(sin(rot/radian)/speed, 0, cos(rot/radian)/speed)如果按下[pygame.K_s]:glTranslate(-sin(rot/radian)/speed, 0, -cos(rot/radian)/speed)如果按下[pygame.K_a]:glTranslate(sin((rot + 90)/radian)/speed, 0, cos((rot + 90)/radian)/speed)如果按下[pygame.K_d]:glTranslate(-sin((rot + 90)/radian)/speed, 0, -cos((rot + 90)/radian)/speed)glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)对于范围内的 i (8):对于范围内的 j(8):立方体(-i*2.5, -4, -j*2.5)pygame.display.flip()pygame.time.wait(10)主要的()

我认为这可以用作 FPS 游戏中的运动和相机,但事实并非如此.

解决方案

这都是顺序问题.OpenGL 是一个状态引擎.每个操作都会改变一个状态.当您执行诸如

def main():pygame.init()屏幕尺寸 = (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)腐烂 = 0速度 = 3弧度 = 57.2958sum_rot_updown = 0current_mv_mat = (GLfloat * 16)()为真:对于 pygame.event.get() 中的事件:如果 event.type == pygame.QUIT:pygame.quit()退出()按下 = pygame.key.get_pressed()glGetFloatv(GL_MODELVIEW_MATRIX, current_mv_mat)glLoadIdentity()#==#用箭头键左右旋转#==#如果按下[pygame.K_LEFT]:glRotatef(速度/2, 0, -1, 0)腐烂 += 1如果按下[pygame.K_RIGHT]:glRotatef(速度/2, 0, 1, 0)腐烂 -= 1#==# 与 WASD 同行#==#如果按下[pygame.K_w]:glTranslate(0, 0, 1/速度)如果按下[pygame.K_s]:glTranslate(0, 0, -1/速度)如果按下[pygame.K_a]:glTranslate(1/速度, 0, 0)如果按下[pygame.K_d]:glTranslate(-1/速度, 0, 0)glMultMatrixf(current_mv_mat)#==#用箭头键上下旋转#==#如果按下[pygame.K_UP]:sum_rot_updown -= 速度/2如果按下[pygame.K_DOWN]:sum_rot_updown += 速度/2glPushMatrix()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)对于范围内的 i (8):对于范围内的 j(8):立方体(-i*2.5, -4, -j*2.5)glPopMatrix()pygame.display.flip()pygame.time.wait(10)主要的()

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.

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.

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.

解决方案

It's all a matter of the order. OpenGL is a state engine. Each operation changes a state. When you do a operation like glTranslatef or glRotatef then the current matrix on the matrix stack is changed.

In OpenGL there are different matrices, like the model view matrix and the projection matrix. 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(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:

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. 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()

这篇关于如何使用 OpenGL 在 3D 空间中获得垂直旋转?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆