-1
$\begingroup$

I'm trying to move my camera around the object, a house, using some keyboard keys and the gluLookAt() function.

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

int win_id;
const float raio = 100;
float camera_x = 0.0f, camera_y = 0.0f, camera_z = raio;  // Posicao inicial da camera
float up_x = 0.0f, up_y = 1.0f, up_z = 0.0f;  // Posicao inicial do Up Vector
float phi = 0, theta = 0;

// Desenha uma piramide de base quadrangular de lado 1 centrado na origem e altura 0.5
void desenhaPiramide(){
    glBegin(GL_QUADS);
        glVertex3f(-0.5, 0, -0.5);
        glVertex3f(-0.5, 0, 0.5);
        glVertex3f(0.5, 0, 0.5);
        glVertex3f(0.5, 0, -0.5);
    glEnd();
    glBegin(GL_TRIANGLES);
        glVertex3f(-0.5, 0, -0.5);
        glVertex3f(-0.5, 0, 0.5);
        glVertex3f(0, 0.5, 0);
    glEnd();
    glBegin(GL_TRIANGLES);
        glVertex3f(-0.5, 0, 0.5);
        glVertex3f(0.5, 0, 0.5);
        glVertex3f(0, 0.5, 0);
    glEnd();
    glBegin(GL_TRIANGLES);
        glVertex3f(0.5, 0, 0.5);
        glVertex3f(0.5, 0, -0.5);
        glVertex3f(0, 0.5, 0);
    glEnd();
    glBegin(GL_TRIANGLES);
        glVertex3f(0.5, 0, -0.5);
        glVertex3f(-0.5, 0, -0.5);
        glVertex3f(0, 0.5, 0);
    glEnd();
}

// Desenha um quadrado de lado 1
void desenhaQuadrado(){
    glBegin(GL_QUADS);
        glVertex3f(-0.5, -0.5, 0);
        glVertex3f(-0.5, 0.5, 0);
        glVertex3f(0.5, 0.5, 0);
        glVertex3f(0.5, -0.5, 0);
    glEnd();
}

// Aqui se capturam as teclas comuns (letras, números e ESC, por exemplo)
void myKeyboard(unsigned char key, int x, int y){
    float camera_speed = 0.1f;
    switch (key){
        case 'a': // Rotação para cima
            phi += camera_speed;
            break;
        case 'w': // Rotação para a esquerda
            theta -= camera_speed;
            break;
        case 'd': // Rotação para baixo
            phi -= camera_speed;
            break;
        case 's': // Rotação para a direita
            theta += camera_speed;
            break;
        case 'r':
            phi = 0.0f;
            theta = 0.0f;
            up_y = 1;
            break;
        case 27:
            glutDestroyWindow(win_id);
            exit(0);
            break;
        default:
            printf("Voce apertou a tecla codigo: %d\n", key);
            break;
    }

    if (theta >= 359){
        theta = 0;
    } else if (theta < 0){
        theta = 359;
    }

    if (phi >= 179){
        phi = 0;
    } else if (phi < 0){
        phi = 179;
    }

    camera_x = raio * cos(theta) * sin(phi);
    camera_y = raio * sin(theta) * sin(phi);
    camera_z = raio * cos(phi);

    glutPostRedisplay();
}

// Aqui se capturam as teclas especiais (Alt, Control, Shift, F1, F2, etc.)
void myKeyboardSpecial(int key, int x, int y ){
    switch (key){
        case GLUT_KEY_UP:
            glutFullScreen(); // Modo tela cheia
            break;
        case GLUT_KEY_DOWN:
            glutReshapeWindow(800, 600); // Modo janela de 800 por 600
            break;
        default:
            printf("Voce apertou a tecla especial codigo: %d\n", key);
            break;
    }
}

// Trecho de codigo para interacoes com o mouse
/*
void myMouse(int button, int state, int x, int y){
    if (button == GLUT_LEFT_BUTTON)
    if (state == GLUT_DOWN){
      float r, g, b;
      r = (double)rand() / (double)RAND_MAX;
      g = (double)rand() / (double)RAND_MAX;
      b = (double)rand() / (double)RAND_MAX;
      glColor3f(r,g,b);
      printf("%.2f, %.2f, %.2f\n", r, g, b);
    }
    glutPostRedisplay();
}
*/

void init(void){
    glClearColor(0.5, 0.9, 0.4, 0.0);

    // Define o modo de rastreio de cor utilizado para misturar a cor do material nos pontos da face (smoothing)
    glShadeModel(GL_SMOOTH);

    // Habilita a definição da cor do material a partir da cor corrente
    glEnable(GL_COLOR_MATERIAL);

    // Parâmetros para a luz ambiente, difusa e especular, definindo também a posição da luz
    GLfloat light_ambient[] = { 0.0, 0.0, 0.0, 1.0 };
    GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light_position[] = { 10.0, 10.0, 10.0, 0.0 };

    // Passa os parâmetros definidos acima para a OpenGL
    glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    // Habilita a luz ambiente, a luz zero e o depth buffer
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_DEPTH_TEST);
}

void display(void){
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    // Define a posição da câmera, para onde ela aponta e qual o vetor UP
    gluLookAt(camera_x, camera_y, camera_z, 0.0f, 0.0f, 0.0f, up_x, up_y, up_z);

    // Base da casa
    glPushMatrix();
        glColor3f(0.392, 0.584, 0.929);
        glScalef(0.75, 1, 1.5);
        glutSolidCube(35);
    glPopMatrix();

    // Porta da casa
    glPushMatrix();
        glColor3f(0.21, 0.27, 0.31);
        glTranslatef(0, -10, 26.50);
        glScalef(8, 15, 1);
        desenhaQuadrado();
    glPopMatrix();

    // Telhado da casa
    glPushMatrix();
        glColor3f(0.804, 0.522, 0.247);
        glTranslatef(0, 17.5, 0);
        glScalef(26.25, 30, 52.5);
        desenhaPiramide();
    glPopMatrix();

    glFlush();
}

void reshape (int w, int h){
    glViewport(0, 0, (GLsizei) w, (GLsizei) h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
        // Se a largura for menor que a altura, a projeção é feita para manter a proporção da janela
        glOrtho(-50, 50, -50*(GLfloat)h/(GLfloat)w, 50*(GLfloat)h/(GLfloat)w, -500.0, 500.0);
    else
        // Se a altura for menor que a largura, a projeção é feita para manter a proporção da janela
        glOrtho(-50*(GLfloat)w/(GLfloat)h, 50*(GLfloat)w/(GLfloat)h, -50, 50, -500.0, 500.0);
}

int main(int argc, char** argv){
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_DEPTH | GLUT_RGB);
    glutInitWindowSize(800, 600);
    glutInitWindowPosition(300, 50);
    win_id = glutCreateWindow("Casa_OpenGL_3D");
    init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(myKeyboard);
    glutSpecialFunc(myKeyboardSpecial);
    // glutMouseFunc(myMouse);
    glutMainLoop();
    return 0;
}

If I press the 'a' or 'd' keys, the movement occurs exactly as it should, as if the object itself were rotating, but in reality, it's just the camera that is moving. An odd detail is that if my first keyboard input is 'w' or 's', nothing happens, but if I press that after having already pressed 'a' or 'd', the movement starts to occur.

The major issue is that this translation isn't happening as I imagined... My idea was as if the house were enclosed by a sphere and the camera positioned on the face/perimeter of that sphere, so that it would only move in that region, enabling viewing the house from all angles and positions. But for some reason, when trying to perform a 360-degree translation, for example, the movement doesn't occur as planned.

It's difficult to explain in detail; it's easier to understand by analyzing the result for yourself. Here is an image exemplifying the result I have in mind.

Camera movement on the 'plane' of the sphere

$\endgroup$

1 Answer 1

0
$\begingroup$

An odd detail is that if my first keyboard input is 'w' or 's', nothing happens, but if I press that after having already pressed 'a' or 'd', the movement starts to occur.

Okay, let's look at what w and s do:

        case 'w': // Rotação para a esquerda
            theta -= camera_speed;

They modify the theta variable. Now, how is theta used?

    camera_x = raio * cos(theta) * sin(phi);
    camera_y = raio * sin(theta) * sin(phi);
    camera_z = raio * cos(phi);

The theta variable has no effect on the camera position if sin(phi) is zero. And the initial value of phi is zero, so sin(phi) is initially zero. That explains it.

In the bigger picture, the problem here is that you've defined the initial state to be phi = 0 to be at the pole of your spherical coordinate system. You probably want the initial state to be at the equator. That means you either:

  • Swap sin(phi) and cos(phi), or
  • Make the initial phi angle be 90° or 270° instead of 0°.

(By the way, the sin and cos functions accept radians, not degrees, but your code is assuming degrees. That won't immediately break things but you should probably fix it.)

Note that there is a completely different way to implement a camera which you might want to use: don't use gluLookAt and instead convert your phi and theta directly into rotations of the modelview matrix. (This is closer to the usual style for first-person cameras.) The advantage of doing that, in this context, is that the keys will always do something even at the pole; changing theta will control the camera's orientation in the world even if that means it's spinning in place at the pole, whereas in your code, at the pole the camera's roll is actually undefined (because the camera position vector is parallel to the up vector). However, this doesn't matter at all if you restrict the angle range to not hit the pole.

$\endgroup$
1
  • $\begingroup$ @peter, I know there are simpler ways to do this, but out of curiosity and for study purposes, I wanted to understand how this works in this situation. I'm also making some modifications to the code based on these responses on Reddit: Post on Reddit . There, I'm posting my code updates in the comments. I'm still having issues with the matter of degrees/radians and with the camera being 'flipped' in certain full rotation movements. $\endgroup$ Commented Nov 8, 2023 at 17:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.