OpenGL is a 2D/3D graphics library. It is based on IRIS GL, developed at Silicon Graphics for their high-end workstations, but is . You can find more information about the OpenGL standard at www.opengl.org. This document is not a tutorial, but introduces some of the basic concepts of OpenGL that will be used in our projects, and provides pointers into the documentation.
The best book for learning OpenGL is the OpenGL Programming Guide, Second Edition, from the OpenGL Architecture Review Board. Copies of this book will be available in the lab in Sieg 228.
You can use the GLTemplate application to experiment with the features of OpenGL.
It common for there to be a family of functions for performing the same operation, differing only in the number and/or types of arguments accepted by each. The names of these functions have tags at the end to indicate the type of argument. The whole family will be referred to with a star syntax. For instance, glColor*() refers to any of the 32 functions available within OpenGL for setting the current color, including:
glColor3f( GLfloat, GLfloat, GLfloat ) |
glColor4d( GLdouble, GLdouble, GLdouble, GLdouble ) |
glColor3ubv( GLubyte* ) |
There is also a library of utility functions whose names all begin with "glu". These are simply convenience functions for doing some common tasks that take a few OpenGL commands to perform.
The OpenGL state is kept in a data structure called a rendering context. We will typically create one rendering context for each OpenGL window. This keeps state changes for drawing in one window from interfering with drawing in the other windows. The Impressionist project, for instance, has two OpenGL windows, one for the original image and one for the painting. We will switch between these windows by calling the wglCurrentContext function to specify which context we want to be the current one.
GL_POINTS | draws each vertex as a point |
GL_LINES | draws a segment connecting the vertices 1 and 2, a segment connecting vertices 3 and 4, 5 and 6, and so on |
GL_TRIANGLES | draws the first three vertices as a triangle, the second group of three as another triangle, and so on |
GL_POLYGON | takes the vertices as a single, simple, convex polygon |
There are several other options, see the manual for more explanation. Note that whenever you tell OpenGL to draw a polygon, it must be flat and convex. Triangles are easy, because they are always flat and convex, but with higher-order polygons you must be careful or your results will look wrong. Each polygon has a front side and a back side, which can be rendered differently if you choose. The front side is defined (by default) as the side from which the vertices appear in counterclockwise order.
The vertices you specify are coordinates in 3D space. They are transformed by the current modelview matrix, then by the current projection matrix to determine where they map to on the screen.
The state maintains a stack for each matrix, with the top matrix being the one that is in force. You can thus make temporary changes by pushing a copy, altering the matrix, using it, then throwing it away with a pop. Since a full stack is maintained, you can nest these temporary changes to (almost) arbitrary depth. The useful commands for manipulating these stacks are:
glMatrixMode | Selects which matrix stack is being manipulated, GL_PROJECTION or GL_MODELVIEW. (Actually, there is a third one for controlling texture mapping, but you can read about that in the manual.) The remaining commands all affect the currently selected matrix stack. |
glLoadIdentity | Replaces the top element of the stack with an identity matrix. |
glPushMatrix | Duplicates the top element of the matrix stack. |
glPopMatrix | Pops and discards the top element of the stack. |
glTranslate* glRotate* glScale* | Composes the top element of the stack with a transformation. |
glReadPixels, glDrawPixels | read/write a block of pixels from/to the frame buffer |
glReadBuffer, glDrawBuffer | select which buffer is read/written |
glPixelStore* | specify how pixels read/written are arranged |
glRasterPos* | set the starting point for pixel write operations |
Keep in mind that we will frequently be using a double-buffered framebuffer to provide smooth animation. With double-buffering, objects are typically drawn into the "back" buffer, which is then swapped with the front buffer (with the SwapBuffers function) to provide the illusion of instantaneous redrawing. It is important to understand which buffer you are reading or writing when doing block pixel operations.