In this tutorial you will see seven simple OpenGL programs each successive one building on the previous one. Here's a short description of each program:
Note: you will be using the Mesa graphics library in the computer lab MC103N. You can use this Makefile to compile 1.c. This Makefile can be easily modified to compile the other sample programs.
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <stdio.h> #include <stdlib.h>
#include <math.h>
#define WIDTH 480 #define HEIGHT 480
#define RED 0 #define GREEN 0 #define BLUE 0 #define ALPHA 1
#define KEY_ESC 27
int main(int argc, char **argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA );
glutInitWindowSize(WIDTH, HEIGHT);
glutInitWindowPosition(0, 0);
glutCreateWindow("Square");
initGL();
init_scene();
glutDisplayFunc(&window_display);
glutReshapeFunc(&window_reshape);
glutKeyboardFunc(&window_key);
glutMainLoop(); return 1; }
GLvoid initGL() { glClearColor(RED, GREEN, BLUE, ALPHA); }
void init_scene() { }
Before a point reaches the screen, it passes through three transformations: modelview, projection and viewport. The modelview transformation transforms world coordinate points to view reference coordinates; in other words, it determines the location of scene objects in relationship to the location of the view reference point. The projection transformation projects the resulting view reference coordinates inside a view volume onto a plane. Then, finally the viewport transformation transforms projected points to screen coordinates.
We may want to modify our viewport and projection transformations in reponse to a window resize. For example, we might want the entire scene to fit inside the window at all times. As a result, we may need to change the size of the viewport based on the new size of the window. However, if our changes to the viewport dimensions also changes the viewport width-height ratio we will find that the width-height ratio of the objects drawn in the viewport will also change accordingly. For example, increasing just the width of the viewport will transform squares into rectangles. To compensate, we will need to change the width-height ratio of the projection view volume to match that of the viewport.
GLvoid window_reshape(GLsizei width, GLsizei height) {
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, (GLdouble)width/(GLdouble)height, 1, 10);
glMatrixMode(GL_MODELVIEW); }
GLvoid window_display() {
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
gluLookAt(0, 0, 5, 0, 0, 0, 0, 1, 0);
render_scene();
glFlush(); }
void render_scene() {
glColor3f(1, 1, 1);
glBegin(GL_POLYGON);
glVertex3f(0, 0, 0); glVertex3f(1, 0, 0); glVertex3f(1, 1, 0); glVertex3f(0, 1, 0);
glEnd(); }
GLvoid window_key(unsigned char key, int x, int y) { switch (key) { case KEY_ESC: exit(1); break; default: printf ("Pressing %d doesn't do anything.\n", key); break; } }
This accomplished by the following composition: Rz(45) * S(0.5, 1, 1) * T(-0.5, -0.5, 0). These translate into three OpenGL function calls. glRotatef(angle, x, y, z) is a rotation transformation about the vector (x,y,z).
Note the order of the calls relative to the composition order...it's very important.
void render_scene() { glRotatef(45, 0, 0, 1); glScalef(0.5, 1, 1); glTranslatef(-0.5, -0.5, 0);
glColor3f(1, 1, 1); glBegin(GL_POLYGON); glVertex3f(0, 0, 0); glVertex3f(1, 0, 0); glVertex3f(1, 1, 0); glVertex3f(0, 1, 0); glEnd(); }
At first there is no transformation. It is the same as the screen shot for the first program.
The red square is rotated by 45 degrees about the z-axis.
The green square is scaled by 50% along the x-axis and then rotated by 45 degrees.
The blue square is translated so that it is centered at (0,0,0) before being scaled and rotated.
We use the space bar to step through the transformations.
#define KEY_SPC 32
int squares = 0;
GLvoid window_key(unsigned char key, int x, int y) { switch (key) { case KEY_ESC: exit(1); break;
case KEY_SPC: squares = (squares+1)%4;
glutPostRedisplay();
break;
default:
printf ("Pressing %d doesn't do anything.\n", key);
break;
}
}
void render_scene() { glColor3f(1, 1, 1); square(); if (squares >= 1) { glRotatef(45, 0, 0, 1); glColor3f(1, 0, 0); square(); } if (squares >= 2) { glScalef(0.5, 1, 1); glColor3f(0, 1, 0); square(); } if (squares >= 3) { glTranslatef(-0.5, -0.5, 0); glColor3f(0, 0, 1); square(); } }
void square() { glBegin(GL_POLYGON); glVertex3f(0, 0, 0); glVertex3f(1, 0, 0); glVertex3f(1, 1, 0); glVertex3f(0, 1, 0); glEnd(); }
GLuint square;
void init_scene() { square = glGenLists(1); glNewList(square, GL_COMPILE); glBegin(GL_POLYGON); glVertex3f(0, 0, 0); glVertex3f(1, 0, 0); glVertex3f(1, 1, 0); glVertex3f(0, 1, 0); glEnd(); glEndList(); }
glCallList(square);
GLuint face; GLuint cube;
GLvoid initGL() { glClearColor(RED, GREEN, BLUE, ALPHA); glClearDepth(1.0); glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); }
glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH);
void init_scene() { face = glGenLists(2); cube = face+1;
glNewList(face, GL_COMPILE); glBegin(GL_POLYGON); glVertex3f(0, 0, 0); glVertex3f(1, 0, 0); glVertex3f(1, 1, 0); glVertex3f(0, 1, 0); glEnd(); glEndList();
glNewList(cube, GL_COMPILE);
glTranslatef(-0.5, -0.5, 0.5);
glColor3f(1, 0, 0);
glCallList(face);
This translation only applies to the rear face so we don't want it to affect the other faces. Consequently, we save the current modelview matrix before performing the translation using glPushMatrix() and then restore this matrix after the rear face is is drawn using glPopMatrix().
glColor3f(1, 1, 0);
glPushMatrix();
glTranslatef(0, 0, -1);
glCallList(face);
glPopMatrix();
glColor3f(0, 1, 0);
glPushMatrix();
glRotatef(90, 0, 1, 0);
glCallList(face);
glPopMatrix();
glColor3f(0, 1, 1);
glPushMatrix();
glTranslatef(1, 0, 0);
glRotatef(90, 0, 1, 0);
glCallList(face);
glPopMatrix();
glColor3f(0, 0, 1);
glPushMatrix();
glRotatef(-90, 1, 0, 0);
glCallList(face);
glPopMatrix();
glColor3f(1, 0, 1);
glPushMatrix();
glTranslatef(0, 1, 0);
glRotatef(-90, 1, 0, 0);
glCallList(face);
glPopMatrix();
glEndList();
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
void render_scene() { glRotatef(20, 0, 1, 0); glRotatef(20, 1, 0, 0); glCallList(cube); }
#define KEY_ESC 27 #define KEY_UP 101 #define KEY_DOWN 103 #define KEY_X 120 #define KEY_Y 121 #define KEY_Z 122
#define DELTA 5 int x = 0; int rotateX = 0; int y = 0; int rotateY = 0; int z = 0; int rotateZ = 0; int speed = 0;
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
glutIdleFunc(&window_idle); glutSpecialFunc(&window_special_key);
glutSwapBuffers();
GLvoid window_key(unsigned char key, int x, int y) { switch (key) { case KEY_ESC: exit(1); break; case KEY_X: rotateX = !rotateX; glutPostRedisplay(); break; case KEY_Y: rotateY = !rotateY; glutPostRedisplay(); break; case KEY_Z: rotateZ = !rotateZ; glutPostRedisplay(); break; default: printf ("Pressing %d doesn't do anything.\n", key); break; } } GLvoid window_special_key(int key, int x, int y) { switch (key) { case KEY_UP: speed = (speed + DELTA + 360) % 360; glutPostRedisplay(); break; case KEY_DOWN: speed = (speed - DELTA + 360) % 360; glutPostRedisplay(); break; default: printf ("Pressing %d doesn't do anything.\n", key); break; } }
GLvoid window_idle() { if (rotateX) x = (x + speed + 360) % 360; if (rotateY) y = (y + speed + 360) % 360; if (rotateZ) z = (z + speed + 360) % 360; if (speed > 0 && (rotateX || rotateY || rotateZ)) glutPostRedisplay(); }
void render_scene() { glRotatef(x, 1, 0, 0); glRotatef(y, 0, 1, 0); glRotatef(z, 0, 0, 1); glCallList(cube); }
void init_scene() { GLUquadricObj *quadric; cylinder = glGenLists(1); glNewList(cylinder, GL_COMPILE); quadric = gluNewQuadric(); gluQuadricDrawStyle(quadric, GLU_FILL); glColor3f(1, 0, 0);
Consider a globe with lines of latitude and longitude drawn on its surface. Notice that these lines subdivide the globe's surface into curved squares and triangles. If we replace these squares and triangles with flat squares and triangles we have just created an object out of convex polygons whose shape is similar to that of a globe. Notice that the more lines of latitude and longitude we have, the more accurately the object resembles a globe.
To see how this applies to cylinders, replace GLU_FILL above with GLU_LINE to draw the cylinder as a wire frame and then try different values for longitude and latitude.
gluCylinder(quadric, 1, 0.75, 1, 15, 15);
glColor3f(0, 1, 0);
gluDisk(quadric, 0.5, 1, 15, 15);
glColor3f(0, 0, 1);
glTranslatef(0, 0, 1);
gluDisk(quadric, 0.5, 0.75, 15, 15);
gluDeleteQuadric(quadric);
glEndList();
}