Abhijit Joshi

ENGINEER. PROGRAMMER. ARTIST.

OpenGL Tutorial 004: Moving Camera

Time to go 3D.

You now learn how to represent a 3D object (with hidden surface removal) using first principles. There exist more elegant ways to implement hidden surface removal, as you will discover in future tutorials.

Next, you will discover how to look at the object from different sides. In effect, the object will remain fixed and the camera (viewing the object) will revolve around the object in a circular orbit.

Finally, you find out how to make GLFW detect keyboard events. We will begin using the ESC key to stop the animations.


#include <GLFW/glfw3.h>  // GLFW
#include <OpenGL/glu.h>  // for using gluLookAt( ... )

// the usual gang of C++ headers

#include <iostream>
#include <cmath>

static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
}

// draw a cube using OpenGL primitives

void drawCube()
{
    glColor3f(1, 0, 0);
    glBegin(GL_POLYGON);
    {  
        glVertex3f( -0.75,  -0.75,  -0.5);  // 0
        glVertex3f(  0.75,  -0.75,  -0.5);  // 1
        glVertex3f(  0.75,   0.75,  -0.5);  // 2
        glVertex3f( -0.75,   0.75,  -0.5);  // 3
    }
    glEnd();

    // draw the coordinate axes

    glBegin(GL_LINES);
    {  
        glColor3f(1, 1, 1);
        glVertex3f( 0.,  0.,  0.);  // x axis
        glVertex3f( 2.,  0.,  0.);

        glVertex3f( 2.,  0.,  0.);
        glVertex3f( 1.8,  0.1,  0.);

        glVertex3f( 2.,  0.,  0.);
        glVertex3f( 1.8,  -0.1,  0.);

        glVertex3f( 0.,  0.,  0.);  // y axis
        glVertex3f( 0.,  2.,  0.);
    }
    glEnd();

    glBegin(GL_LINES);
    {  
        glVertex3f( -0.75,  -0.75,  -0.5);  // 0
        glVertex3f( -0.75,  -0.75,   0.5);  // 4

        glVertex3f(  0.75,  -0.75,  -0.5);  // 1
        glVertex3f(  0.75,  -0.75,   0.5);  // 5

        glVertex3f(  0.75,   0.75,  -0.5);  // 2
        glVertex3f(  0.75,   0.75,   0.5);  // 6

        glVertex3f( -0.75,   0.75,  -0.5);  // 3
        glVertex3f( -0.75,   0.75,   0.5);  // 7
    }
    glEnd();

    glColor3f(0, 1, 0);
    glBegin(GL_POLYGON);
    {
        glVertex3f( -0.75,  -0.75,   0.5);  // 4
        glVertex3f(  0.75,  -0.75,   0.5);  // 5
        glVertex3f(  0.75,   0.75,   0.5);  // 6
        glVertex3f( -0.75,   0.75,   0.5);  // 7
    }
    glEnd();

    glBegin(GL_LINES);
    {
        glColor3f(0, 0, 1);
        glVertex3f( 0.,  0.,  0.5);  // z axis
        glVertex3f( 0.,  0.,  2.);
    }
    glEnd();
}

// calculate pixel colors for the current graphics window

void display(float scale, int time)
{
    // select background color to be black
    float R = 0, G = 0, B = 0, alpha = 0;
    glClearColor(R, G, B, alpha);

    // replace current transformation matrix with the identity matrix
    glLoadIdentity();

    // set clipping planes in the X-Y-Z coordinate system
    float xmin = -scale, xmax = scale;
    float ymin = -scale, ymax = scale;
    float zmin = -20*scale, zmax = 20*scale;
    glOrtho(xmin, xmax, ymin, ymax, zmin, zmax);

    // new screen coordinates in OpenGL
    //
    //   (xmin, ymax)                     (xmax, ymax)
    //           +-----------+-----------+
    //           |           |           |
    //           |           |           |
    //           |           |           |
    //           |           |           |
    //           |           | (0,0)     |
    //           +-----------+-----------+
    //           |           |           |
    //           |           |           |
    //           |           |           |
    //           |           |           |
    //           |           |           |
    //           +-----------+-----------+
    //  (xmin, ymin)                      (xmax, ymin)

    // position the camera at a certain point and check how the view looks

    float angle = time*0.001;

    gluLookAt (cos(angle), sin(angle), 1.0,    // camera position at (x,y,z)
                      0.0,        0.0, 0.0,    // camera looks towards point (x,y,z)
                      0.0,        0.0, 1.0);   // the "up" vector

//  std::cout << "Viewing from point (" << cos(angle) << ", " << sin(angle) << ", " << 1.0 << ")\n";

    // clear all pixels in the window with the current color
    glClear(GL_COLOR_BUFFER_BIT);

    // draw a cube using OpenGL primitives
    drawCube();
}

int main(int argc, char* argv[])
{
    //--------------------------------
    //   Create a WINDOW using GLFW
    //--------------------------------

    GLFWwindow *window;

    // initialize the library
    if(!glfwInit())
        return -1;

    // window size for displaying graphics
    int WIDTH  = 600; //atoi(argv[1]);
    int HEIGHT = 600; //atoi(argv[2]);

    // set the window's display mode
    window = glfwCreateWindow(WIDTH, HEIGHT, "Triangle", NULL, NULL);
    if(!window)
    {
        glfwTerminate();
    return -1;
    }

    // make the windows context current
    glfwMakeContextCurrent(window);

    // enable quitting the graphics by pressing ESC
    glfwSetKeyCallback(window, key_callback);

    //---------------------------------------
    // Loop until the user closes the window
    //---------------------------------------

    float scale = 3, delta = 0.01;
    int time = 0;
    while(!glfwWindowShouldClose(window))
    {
        // this is where you draw something
        display(scale, time);

        // swap front and back buffers
        glfwSwapBuffers(window);

        // poll for and processs events
        glfwPollEvents();

        // adjust scale
        scale += delta;
        if ( (scale > 4) || (scale < 2) ) delta = -delta;

        // increment time
        time += 3;
    }

    glfwDestroyWindow(window);
    glfwTerminate();
    return 0;
}

To compile this code, use the following Makefile:

NAME = movingCamera

all:
    g++ -I /usr/local/include/ -L /usr/local/lib ${NAME}.cpp -o cpp.x -framework OpenGL -framework Cocoa -framework IOKit -lglfw

clean:
    rm *.x