Search code examples
openglglut

OpenGL and gluLookAt function


I have function moveCamera() that rotate camera around center sphere.

void moveCamera()
{
    glLoadIdentity();
    int vec = ceil(theta / 3.1415);
    int y;
    if (vec%2)
        y = 1;
    else
        y = -1;
    gluLookAt(e_x, e_y, e_z, 0, 0, 0, 0, y, 0);
}

This function is called inside display()

void display()
{
    glClearColor(0, 0, 0, 1);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    moveCamera();

    glCallList(idList);

    GLfloat pos[] = {0, 0, 0, 1};
    glLightfv(GL_LIGHT0, GL_POSITION, pos);

    glFinish();
}

In this situation all works fine. But I noticed some things, that I cannot explain.

For example, if I modify display() function for drawing red line in the direction X-axis like this

void display()
{
    glClearColor(0, 0, 0, 1);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    glColor3ub(255, 0, 0);
    glLineWidth(5);
    glBegin(GL_LINES);
        glVertex3i(0, 0, 0);
        glVertex3i(1000, 0, 0);
    glEnd();

    moveCamera();

    glCallList(idList);

    GLfloat pos[] = {0, 0, 0, 1};
    glLightfv(GL_LIGHT0, GL_POSITION, pos);

    glFinish();
}

... nothing happened. Why???

Another, if I remove glLoadIdentity() after glClear() the red line will draw, but when I move the camera by keyboard buttons, I see some bug in rendering this line (the line change their coordinates for drawing). For see it, run my code.

The full code:

#include <iostream>
#include <glut.h>
#include <math.h>
#include <QDebug>
using namespace std;

#define WIDTH 1024
#define HEIGHT 600

void display();
void reshape(int width, int height);
void keyboard(unsigned char key, int x, int y);
void moveCamera();
void init();
void mkList();
void enableLight();
//double z(const double &x, const double &y);
void printMatrix(double *m)
{
    for (int i = 0; i < 4; i++)
        qDebug() << QString("%1   %2   %3   %4").arg(m[i*4]).arg(m[i*4+1]).arg(m[i*4+2]).arg(m[i*4+3]);
    qDebug() << "\n";
}

GLuint idList = 0;

double e_x = 0;
double e_y = 0;
double e_z = 0;
double r = 300;
double phi = 0;
double theta = 1.5;

int main(int argc, char **argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(WIDTH, HEIGHT);
    glutInitWindowPosition(30, 100);
    glutCreateWindow("Lab#2");
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);

    glEnable(GL_DEPTH_TEST);
    init();
    mkList();
    enableLight();

    glutMainLoop();
    return 0;
}

void display()
{
    glClearColor(0, 0, 0, 1);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    glColor3ub(255, 0, 0);
    glLineWidth(5);
    glBegin(GL_LINES);
        glVertex3i(0, 0, 0);
        glVertex3i(1000, 0, 0);
    glEnd();

    moveCamera();

    glCallList(idList);

    GLfloat pos[] = {0, 0, 0, 1};
    glLightfv(GL_LIGHT0, GL_POSITION, pos);

    glFinish();
}

void reshape(int width, int height)
{
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45, 2, 100, 2000);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void keyboard(unsigned char key, int x, int y)
{
#define ESCAPE '\033'

    switch (key)
    {
    case ESCAPE:
        exit(0);
        break;
    case 's':
        theta += 0.1;
        break;
    case 'w':
        theta -= 0.1;
        break;
    case 'a':
        phi += 0.1;
        break;
    case 'd':
        phi -= 0.1;
        break;
    case 'q':
        r-=5;
        break;
    case 'e':
        r+=5;
        break;
    default:
        break;
    }
    e_x = r * sin(theta) * cos(phi);
    e_z = r * sin(theta) * sin(phi);
    e_y = r * cos(theta);
    glutPostRedisplay();
}

void moveCamera()
{
    glLoadIdentity();
    int vec = ceil(theta / 3.1415);
    int y;
    if (vec%2)
        y = 1;
    else
        y = -1;
    gluLookAt(e_x, e_y, e_z, 0, 0, 0, 0, y, 0);
}

void init()
{
    e_x = r * sin(theta) * cos(phi);
    e_z = r * sin(theta) * sin(phi);
    e_y = r * cos(theta);
}

void mkList()
{
    int idInnerList = glGenLists(1);
    glNewList(idInnerList, GL_COMPILE);

    glColor3ub(255, 0, 0);
    glPushMatrix();
    glTranslatef(-200, 0, 0);
    glutWireSphere(50, 10, 10);
    glPopMatrix();

    glColor3ub(0, 255, 0);
    glPushMatrix();
    glTranslatef(200, 0, 0);
    glutSolidCube(100);
    glPopMatrix();

    glEndList();

    idList = glGenLists(1);
    glNewList(idList, GL_COMPILE);
    glCallList(idInnerList);

    glPushMatrix();
    glTranslatef(0, 0, 200);
    glCallList(idInnerList);
    glPopMatrix();

    glPushMatrix();
    glTranslatef(0, 0, -200);
    glCallList(idInnerList);
    glPopMatrix();
    glEndList();
}

void enableLight()
{
    glEnable(GL_LIGHTING);

    glEnable(GL_COLOR_MATERIAL);
    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);

    GLfloat diffuse[] = {0.7, 0.7, 0.7, 1};

    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);

    glEnable(GL_LIGHT0);
}

Solution

  • For a start, I want to say this: it's 2016 - don't use legacy OpenGL. Read up on modern OpenGL with the help of your favorite search engine.

    If by "nothing happened" you mean "i couldn't see the red line" and if I understood your code correctly, that's just because the line will be entirely clipped. Let me explain.

    The first thing you do that affects vertex transformation inside display() is call glLoadIdentity(). This will set the top of the current matrix stack to the identity matrix, I. Let's assume the current stack is MODELVIEW and the current projection matrix, P, is the one set in reshape(), then your line will undergo the following transformation from local-space into clip-space:

    Vx_clip = P * I * Vx_local
    Vy_clip = P * I * Vy_local
    

    Given that the model-view matrix is the identity, both local coordinates will remain the same in world-space before being transformed into clip-space. Since your near plane is at z=-100, the line will be completely out of the frustum, i.e. not in the space between the near- and the far-plane, and thus be clipped entirely. Mathematically this is a little more involved, but it's conceptually accurate.

    You can convince yourself by defining the red line like this

    glVertex3i(-500, 0, -100);
    glVertex3i(500, 0, -100);
    

    Now, part of the line will be inside inside your frustum, even with an identity model-view matrix, because it partially intersects the near plane and thus must not be entirely clipped.

    If you apply a non-identity model-view matrix first by calling moveCamera() before rendering the red-line, you'll also be able to partially see the red line. Why? Well, let's assume your polar coordinates are chosen so that the camera will still look in the direction (0, 0, -z), then with the given radius, you define the camera to be at (0, 0, 300) in world-space, which gluLookAt() will map to a transformation matrix which will simply add a translation of (0, 0, -300) to both vertices.

    Given the initial definition of your line and the new model-view matrix the vertices of the line will be at (0, 0, -300) and (1000, 0, -300) after left-multiplying the model-view matrix. After transformation to clip-space by left-multiplying the projection matrix, the line will be only partially clipped and thus be visible.

    As I said, this is much more involved and in fact, this is only part of the complete vertex transformation pipeline. Read this and this for a mathematical treatise of the subject of vertex transformation. It's computer-graphics 101. And please: focus on the math, not on the use of the legacy API that you'll unfortunately see there.