Before we start drawing 3D objects, lets quickly go over some fundamentals. You will find drawing to a 3D canvas is in fact very similar to plain old 2D applications. This is because OpenGL uses a coodinate system called the Cartesian plane. The difference here is that instead of having two axis which stretch horizontally and vertically, we add a third axis, the z (or depth) axis. You can think of this as a line which runs through the origin (0,0 on a Cartesian coordinate system), going in the direction from away from you to straight towards you (in OpenGL, the positive z-axis always points towards you, while the negative points away). Using this system, we can represent a point in 3D space, called a "vertex", with three coordinites, representing x, y, and z. For example:
(0,0,0) ← The origin, the center of our defined space.
(2,0,4) ← 2 units to the right, 4 units towards us, on the center of the y-axis.
(3,-4,-2) ← 3 units to the right, 4 units down, and 2 units away from us.
Got the hang of it? Then lets plot some points with OpenGL. We can use the function glVertex to specifiy vertices, flanked by calls to glBegin and glEnd:
glBegin(GL_POINTS); /* We want to draw points */
glVertex3f(2.0, 0.0, 4.0); /* Specify a couple of vertices */
glVertex3f(3.0, –4.0, –2.0);
glEnd(); /* End points */
As you can see, glBegin tells OpenGL we want to start drawing (as well as WHAT we want to start drawing), and glEnd tells it to stop. The great thing about OGL's method of 3D drawing is it's flexibility – lets say we want to draw some lines:
glBegin(GL_LINES); /* lets do some lines now */
glVertex3f(6.0, 4.0, 2.0); /* here's one */
glVertex3f(2.0, –4.0, 3.3);
glVertex3f(5.0, 8.0, 8.0); /* here's another */
glVertex3f(-4.7, 5.0, –3.0);
glVertex3f(0.0, 0.0, 0.0); /* and another! */
glVertex3f(6.0, –1.0, –7.0);
glEnd();
Note that we now have one line for every two vertices. If you specify an odd number, the last vertex is ignored.
Now lets do some shapes. OpenGL specifies 6 different polygon primitives: GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUADS, GL_QUAD_STRIP, and GL_POLYGON. Triangle and quad strips are shortcuts for building polygons next to each other, and likewise a triangle fan is a group of triangles that share a center point. GL_POLYGON can specify a general polygon with any number of vertices. The one you should use most often is GL_TRIANGLES, since most graphic accelerators are optimized for triangle operations. Here's an example of a generic triangle:
glBegin(GL_TRIANGLES);
glVertex3f(-3.0, –3.0, 2.0);
glVertex3f(3.0, –3.0, –1.0);
glVertex3f(0.0, 3.0, 4.0);
glEnd();
See? Nothing to it!
Now, lets take what we've learned so far and draw some 3D using OpenGL. As you can see, the following program bears more than a passing resemblence to the previous one (GLUT is so nice that way), with some changes to the display() function. One thing you will notice is that I change the current color with glColor before specifiying some of the vertices. When OpenGL sees a polygon with vertices that have different colors, it draws the figure by smoothly shading from one color to the next. In this example I've created an abstract shape made out of one square surrounded by four triangles. One point of the triangles is red, while the other two are blue. This creates a smoothing purple effect across the face of the triangle.
I'm also using this example to demonstrate some of the UI routines that GLUT uses. In this case we are going to be using the function glutKeyboardFunc(). This function defines a callback handler that will be called whenever a key is pressed while our window has focus.
/**********************************************************************/
/******************************************/
/* Example 2: Drawing in 3D */
/******************************************/
#include
#include
#include
void init(void);
void display(void);
void keyboard(unsigned char, int, int);
int main (int argc, char **argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(600, 600);
glutInitWindowPosition(50, 50);
glutCreateWindow("A 3D Object");
init();
glutDisplayFunc(display);
glutKeyboardFunc(keyboard); /* set keyboard handler */
glutMainLoop();
return 0;
}
void init(void) {
glClearColor(0.0, 0.0, 0.0, 0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-15.0, 15.0, –15.0, 15.0, –15.0, 15.0);
}
void display(void) {
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_QUADS);
glColor3f(0.0, 0.0, 1.0); /* center square */
glVertex3f(-3.0, –3.0, 0.0);
glVertex3f(3.0, –3.0, 0.0);
glVertex3f(3.0, 3.0, 0.0);
glVertex3f(-3.0, 3.0, 0.0);
glEnd();
glBegin(GL_TRIANGLES);
glColor3f(1.0, 0.0, 0.0); /* now draw the four triangles */
glVertex3f(0.0, 6.5, 0.0);
glColor3f(0.0, 0.0, 0.9f);
glVertex3f(-3.0, 3.0, 0.0);
glVertex3f(3.0, 3.0, 0.0);
glColor3f(0.0, 0.0, 0.9f);
glVertex3f(-3.0, –3.0, 0.0);
glVertex3f(-3.0, 3.0, 0.0);
glColor3f(1.0, 0.0, 0.0);
glVertex3f(-6.5, 0.0, 0.0);
glColor3f(1.0, 0.0, 0.0);
glVertex3f(0.0, –6.5, 0.0);
glColor3f(0.0, 0.0, 0.9f);
glVertex3f(3.0, –3.0, 0.0);
glVertex3f(-3.0, –3.0, 0.0);
glColor3f(1.0, 0.0, 0.0);
glVertex3f(6.5, 0.0, 0.0);
glColor3f(0.0, 0.0, 0.9f);
glVertex3f(3.0, 3.0, 0.0);
glVertex3f(3.0, –3.0, 0.0);
glEnd();
glutSwapBuffers();
}
void keyboard(unsigned char key, int x, int y) {
/* this is the keyboard event handler the x and y parameters are the mouse coordintes when the key was struck */
switch (key) {
case 'u':
case 'U':
glRotatef(3.0, 1.0, 0.0, 0.0); /* rotate up */
break;
case 'd':
case 'D':
glRotatef(-3.0, 1.0, 0.0, 0.0); /* rotate down */
break;
case 'l':
case 'L':
glRotatef(3.0, 0.0, 1.0, 0.0); /* rotate left */
break;
case 'r':
case 'R':
glRotatef(-3.0, 0.0, 1.0, 0.0); /* rotate right */
}
display(); /* repaint the window */
}
/**********************************************************************/
Note that I use the glRotate function to rotate the view when the u, d, l, or r keys are pressed; don't worry, we haven't gone over this yet. I'll cover it in the next section, for now I put it in to illustrate the 3D nature of our shape. You may want to play around with this demo before continuing on to the next section. What else can you draw? What happens to the shading in the demo if you call glShadeModel(GL_FLAT)? Once you feel comfortable working with 3D space, we can move on to the next section…