| CS 381 Fall 2012 > Lecture Notes for Thursday, September 6, 2012 |
All rendering operations are composed of primitives.
OpenGL primitives are of two kinds:
glRect, glBitmap, glDrawPixels).We now consider the second category. There are ten general geometry primitives in OpenGL.
GL_POINTSGL_LINESGL_LINE_STRIPGL_LINE_LOOPGL_TRIANGLESGL_TRIANGLE_STRIPGL_TRIANGLE_FANGL_QUADSGL_QUAD_STRIPGL_POLYGONBelow are illustrations of these primitives. The colored regions illustrate what is drawn. Small circles represent vertices; the numbers show the order in which the vertices are given.
Primitive GL_POINTS
draws a point for each specified vertex.
Primitive GL_LINES
takes an even number of vertices,
drawing a line segment between vertices 1-2, then 3-4, then 5-6, etc.
Primitive GL_LINE_STRIP
draws a line segment between each consecutive pair of vertices:
1-2, then 2-3, then 3-4, etc.
Primitive GL_LINE_LOOP
is just like GL_LINE_STRIP,
except that it also draws a line segment between the first and last vertices.
Primitive GL_TRIANGLES
is similar to GL_LINES.
It makes triangles out of triples of vertices:
1-2-3, then 4-5-6, then 7-8-9, etc.
Primitive GL_TRIANGLE_STRIP
is similar to GL_LINE_STRIP.
It make a triangle out of each consecutive triple of vertices:
1-2-3, then 2-3-4, then 3-4-5, etc.
Primitive GL_TRIANGLE_FAN
makes triangles using the first vertex
and each consective pair in the remaining vertices:
1-2-3, then 1-3-4, then 1-4-5, etc.
Primitive GL_QUADS
is similar to GL_TRIANGLES.
It makes quadrilaterals out of quadruples of vertices:
1-2-3-4, then 5-6-7-8, etc.
Primitive GL_QUAD_STRIP
is almost exactly like GL_TRIANGLE_STRIP.
It makes quadrilaterals, rather than triangles,
but since a quadrilateral is two triangles stuck together,
there might be no difference at all.
(Note that the order of the vertices around a quadrilateral is
different from that for GL_QUADS:
1-2-4-3 vs. 1-2-3-4.)
Primitive GL_POLYGON
draws a single polygon using all given vertices.
The result is very similar to the output of GL_TRIANGLE_FAN
(and might be exactly the same).
Know these ten primitives, including the associated vertex orderings!
Quadrilaterals and general polygons (last 3 primitives above) must be:
OpenGL has a number of methods of specifying primitives.
We look at the method that existed in the first OpenGL release:
glBegin-glEnd.
Note: The glBegin-glEnd
method has since been deprecated (in OpenGL 3.0)
and then removed (from OpenGL 3.1).
However, I have never seen an OpenGL implementation
that did not include it.
Later in the semester, we will look at more “modern”
methods: vertex arrays and vertex buffer objects.
These methods reduce the overhead in transfering data
to the graphics hardware.
However, they are (IMHO) significantly more difficult to use,
and they make no new functionality available.
We begin a primitive with a glBegin call,
passing the name of the primitive as a parameter.
We end the primitive with a glEnd call.
Between these, we specify vertices and, optionally,
their attributes.
Typical code:
glBegin(GL_TRIANGLES);
glVertex2d(0., 0.);
glVertex2d(1., 0.);
glVertex2d(1., 1.);
glEnd();
Only a very limited set of GL commands can be executed between
glBegin and
glEnd,
where “between”
does not refer to the structure of your code,
but only the order in which OpenGL commands are executed.
Of the commands we have seen,
only glVertex* and glColor* are legal.
The glVertex* command does two things:
Note that this means that attributes of a vertex
(its color, for example)
must be set before the glVertex* command.
OpenGL has facilities for drawing bitmaps, and GLUT can use these to make text in the graphics window. (GLUT text is rather low-quality by today’s standards; we will not spend much time on it.)
To make a character, set the raster position
using glRasterPos*,
and than call glutBitmapCharacter
with 2 parameters: the character set
(I prefer GLUT_BITMAP_9_BY_15)
and the character (a char).
This advances the raster position,
so that successive calls to glutBitmapCharacter
will place characters in a line, as usual.
I have written a class to make
GLUT text convenient.
See
bitmapprinter.h.
See
textkbd.cpp
for a program that uses this class.
This concludes our unit on The Basics of Computer Graphics. Now we begin the second unit: Animation & Interaction.
Topics in this unit:
Programs that have a graphical user interface (GUI—say “gooey”) are almost always event-driven. An event is something that happens, often originating with the user, that the program can respond to.
Some examples of events (these can vary from system to system):
A typical GUI program does its initialization, makes a window, and then enters its event loop. In this loop, it waits for an event, responds, when it gets one, by executing the appropriate handler, and then goes back to waiting.
A response to an event will often require a change in the contents of a window; the window must be redrawn. However, event handlers (other than those that handle window redisplay events) should generally not do any drawing. Instead, they:
Later, when the redisplay event is processed, the window contents will be redrawn, using the above-mentioned data.
In a GLUT program, you do not write the event loop.
Instead you call glutMainLoop,
which does not return.
Event handlers should be registered with GLUT
before calling glutMainLoop.
GLUT generates a redisplay event when a window is created, when part needs to be redrawn, or when you post such an event. Such events are handled by a display function, which takes no parameters and returns nothing.
void myDisplay()
{
...
To register a display function,
call glutDisplayFunc with a pointer to the display function.
glutDisplayFunc(myDisplay);
You can also post your own redisplay events.
To do this, call
glutPostRedisplay,
which takes no parameters.
glutPostRedisplay();
When no other event needs to be handled,
GLUT generates a null event,
which is handled by an idle function.
This also takes no parameters and returns nothing.
Register an idle function with
glutIdleFunc.
glutIdleFunc(myIdle);
See any of the posted programs for examples of display and idle functions.
You cannot add parameters to GLUT callbacks. In particular, the display function has none. Thus, the data describing the window contents must be stored in global variables. Here is how we make an event update the window contents, in a GLUT program.
init? in a constructor?).
glutPostRedisplay.
As a memory aid, note that the intials above spell “DICE”.
In a GLUT program, the keyboard function
handles ASCII keypresses.
Register this with glutKeyboardFunc.
The keyboard function takes 3 parameters:
the key pressed
(an unsigned char),
and the x and y coordinates
of the mouse at the time of the keypress,
(in pixels, from the upper-left-hand corner
of the drawing area of the window, as ints).
It returns nothing.
void myKeyboard(unsigned char key, int x, int y)
{
switch (key)
{
case 'a':
case 'A':
// handle A-key press here
glutPostRedisplay();
break;
...
In a GLUT program, the special function
handles non-ASCII keypresses (e.g., arrow keys).
Register this with glutSpecialFunc.
The signature of the special function is identical to
that of the keyboard function,
except that the first parameter is an int.
This int will be
one of a number of special constants defined in
glut.h.
The names of these constants are of the form
“GLUT_KEY_...”
void mySpecial(int key, int x, int y)
{
switch (key)
{
case GLUT_KEY_RIGHT: // right arrow key
// handle right-arrow-key press here
glutPostRedisplay();
break;
...
We added more keyboard handling to
textkbd.cpp (which see).
ggchappell@alaska.edu