How do I deal with OpenGL's function of gluLookAt successfully?

1.2k views Asked by At

Trying to implement a 3D roaming camera in my python and pygame 3D OpenGL game is something I've been trying to do, but I hit a roadblock. Every time I try to use the gluLookAt function, the camera just freaks out and yanks around while the x and z coordinates increase rapidly and switching from negative to positive. The game I am trying to make is just a flat surface that you walk around and on. I have all controls implemented for moving around, but not the camera.

Here is some summarized code for those who don't want to read all my coding:

while True:
    matrix = glGetDoublev(GL_MODELVIEW_MATRIX)
    camera_x = matrix[3][0]
    camera_y = matrix[3][1]
    camera_z = matrix[3][2]

    # the 3-6 ones are what the camera is looking at
    gluLookAt(camera_x, camera_y, camera_z, 0, 0, 0, 0, 1, 0)
    print("({}, {}, {})".format(int(-camera_x), int(-camera_y), int(-camera_z)))

If the problem cannot be solved with the code above, here is my full(ish) code:

import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
import os

speed = .5

def enviroment():
    glBegin(GL_QUADS)
    for surface in surfaces:
        for vertex in surface:
            glColor3fv((1, 0, 0))
            glVertex3fv(verticies[vertex])
    glEnd()

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


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

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

    glTranslatef(0.0, -3, -150)

    glRotatef(0, 0, 0, 0)

    while True:
        press = pygame.key.get_pressed()

        matrix = glGetDoublev(GL_MODELVIEW_MATRIX)
        camera_x = matrix[3][0]
        camera_y = matrix[3][1]
        camera_z = matrix[3][2]

        # the 3-6 ones are what the camera is looking at
        gluLookAt(camera_x, camera_y, camera_z, 0, 0, 0, 0, 1, 0)

        print("({}, {}, {})".format(int(-camera_x), int(-camera_y), int(-camera_z)))

        if press[pygame.K_UP]:
            glTranslatef(0, -speed, 0)
        elif press[pygame.K_DOWN]:
            glTranslatef(0, speed, 0)

        elif press[pygame.K_w]:
            glTranslatef(0, 0, speed)
        elif press[pygame.K_s]:
            glTranslatef(0, 0, -speed)
        elif press[pygame.K_a]:
            glTranslatef(speed, 0, 0)
        elif press[pygame.K_d]:
            glTranslatef(-speed, 0, 0)

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            elif event.type == KEYDOWN:
                if event.key == K_UP:
                    glTranslatef(0, -speed, 0)
                elif event.key == K_DOWN:
                    glTranslatef(0, speed, 0)

                elif event.key == K_w:
                    glTranslatef(0, 0, speed)
                elif event.key == K_s:
                    glTranslatef(0, 0, -speed)

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

main()

Any help would be appreciated!

1

There are 1 answers

0
codereptile On

First of all, I don't see for what reasons you use pygame. OpenGL and GLUT (OpenGL Utility Toolkit) combined have almost everything you might need for 3D graphics in them (especially at the beginner's level). So if you want to start in 3D graphics - GLUT is pretty much everything you need.

About gluLookAt function: The function itself takes 9 parameters and if you want to control the camera's position - you need to operate with the first three:

gluLookAt(eyeX, eyeY, eyeZ, ...)

To rotate the camera around the object (stationed at 0, 0, 0) you need to change the eyeX, eyeY, and eyeZ so that the sum of their squares would be 1 (or some other value, depending on how far from the object the camera should be):

eyeX^2 + eyeY^2 + eyeZ^2 = 1

This is needed to be satisfied because this is an equation of a sphere in 3D.

To start, you can set eyeY variable to 0, so that the camera would rotate in a plane around the object only. Then we need to satisfy this statement:

eyeX^2 + eyeZ^2 = 1

This is an equation of a projection of a sphere to 2D - a circle

Let us introduce an angle α, which will represent our camera's position. To control how the camera revolves around the object, we will change this angle.

Angle image (the image is viewed from above)

So now, we need to convert this angle to coordinates, to use in OpenGL's function.

This can be done using trigonometric functions. Sin(α) will represent our Z-axis value, Cos(α) - our X-axis value. This code would convert our angle to coordinates:

eyeZ = math.sin(alpha)
eyeX = math.cos(alpha)

So now we just need to use gluLookAt function:

gluLookAt(eyeX, 0, eyeZ, 0, 0, 0, 0, 1, 0)

To move the camera you need to change the α angle in any way you prefer.

To do more complex camera management you will need to solve not the circle equality, but the sphere equality and use two angles instead of one.

This is a hard task, but I can recommend you to see the TRON project (https://github.com/leviathan117/TRON/). This is a 3D graphics Python library for beginners, and it has lots of these tasks already written, so you can either use the library itself or open the library's code to see how everything works from the inside.