CS 381 Fall 2011 > Lecture Notes for Thursday, September 8, 2011 |
All rendering operations are composed of primitives.
OpenGL primitives are of two kinds:
glRect
, glBitmap
, glPixmap
).glBegin
-glEnd
.
They produce:
glBegin
-Style Primitives
We now consider the second category.
There are ten glBegin
-style primitives.
GL_POINTS
GL_LINES
GL_LINE_STRIP
GL_LINE_LOOP
GL_TRIANGLES
GL_TRIANGLE_STRIP
GL_TRIANGLE_FAN
GL_QUADS
GL_QUAD_STRIP
GL_POLYGON
Below are illustrations of these primitives.
The colored regions illustrate what is drawn.
Small circles represent vertices;
the numbers show the order in which the glVertex*
calls are made.
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:
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.
Note: The glBegin
-glVertex*
-glEnd
method is very flexible,
but it can have high overhead, due to the large number of function calls.
There are more efficient mechanisms for getting vertex data
to the pipeline.
You may wish to look into
vertex arrays and vertex buffer objects.
However, while these can improve speed in some cases,
they do not add any new capabilities.
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
textkbd.cpp
.
This concludes our unit on The Basics of Computer Graphics. Now we begin the second unit: Animation & Interaction.
Major Topics:
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
.
For example,
GLUT generates a reshape event when the size of a window is set,
that is, when the window is created or resized.
Such events are handled by a reshape function,
which takes two parameters (int
s giving the new
width and height of the drawing area of the window, in pixels)
and returns nothing.
void myReshape(int w, int h);
To register a reshape function,
call glutReshapeFunc
with a pointer to the function.
glutReshapeFunc(myReshape);
See
textkbd.cpp
for an example of a simple reshape function.
Drawing is done when GLUT signals a display event.
Register a display function with
glutDisplayFunc
.
glutDisplayFunc(myDisplay);
You can also post your own display events.
To do this, call
glutPostRedisplay
,
which takes no parameters.
glutPostRedisplay();
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. Thus, 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, 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).
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
should be
one of a number of special values defined in
glut.h
.
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 keyboard handling to
textkbd.cpp
(which see).
ggchappell@alaska.edu