You are on page 1of 7

Chapter 6

Attributes of graphics primitives and State Management


Recall that OpenGL is a state system: it maintains a list of current state variables that are used to
modify the appearance of each primitive as it is displayed. Therefore these state variables
represent the attributes of the primitives. The values of state variables remain in effect until new
values are specified, and changes to state variables affects primitives drawn after the change.

1. Colour Attribute
In our first OpenGL program in Chapter 3 we defined values for two colour state variables: the
drawing colour and the background colour. We also saw that we can define colours in two
different ways:

 RGB: the colour is defined as the combination of its red, green and blue components.
 RGBA: the colour is defined as the combination of its red, green and blue components
together with an alpha value, which indicates the degree of opacity/transparency of the
colour.
In the simple program in Chapter 3, the background colour was defined with an alpha value,
whereas the drawing colour did not. If no alpha value is defined it is assumed to be equal to 1,
which is completely opaque.

It can often be useful to combine colours of overlapping objects in the scene or of an object with
the background colour. For example, this is necessary to achieve transparency effects and also
for antialiasing. This process is known as colour blending, and the RGBA colour representation is
necessary to achieve colour blending.

1.1. OpenGL Colour Attributes


We have already seen how to modify the drawing and background colours in the simple OpenGL
program in Chapter 3. To recap, we change the current drawing colour using the glColor*
function and the current background colour using the glClearColor function. The background
colour is always set using an RGBA colour representation (although, as we will see below, the
alpha value is sometimes ignored). The drawing colour can be set using either RGB or RGBA
representations. The following are all valid examples of setting the drawing and background
colours:

glColor3f(1.0,0.0,0.0); // set drawing colour to red

glColor4f(0.0,1.0,0.0,0.05); // set drawing colour to semi-transparent green

glClearColor(1.0,1.0,1.0,1.0); // set background colour to opaque white


Before we do any drawing in OpenGL, we must define a frame buffer. If we want to do colour
drawing then we must specify that we want a colour frame buffer. Again, we saw how to do this
in Chapter 3, but to recap the following line defines a single direct storage colour frame buffer
with red, green and blue components:

glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB)

In order to store alpha values in the frame buffer we must change the second argument to
GLUT_RGBA. To specify that we want to use a colour look-up table we set the second argument
equal to GLUT_INDEX.

2. Point Attributes
Points are the simplest type of primitive so the only attributes we can modify are the colour and
size of the point. The simple program given in Chapter 3 showed how to modify each of these
state variables in OpenGL. To change the size of points in pixels we use:

glPointSize(size);

Points are drawn in the current drawing colour. All points in OpenGL are drawn as squares with
a side length equal to the point size. The default point size is 1 pixel.

3. Line Attributes
The attributes of line primitives that we can modify include the following:

 Line colour
 Line width
 Line style (solid/dashed etc)
We have already seen how to change the drawing colour. In the following sections we examine
the line width and line style attributes.

3.1. Line Width


The simplest and most common technique for increasing the width of a line is to plot a line of
width 1 pixel, and then add extra pixels in either the horizontal or vertical directions. This
concept is illustrated in Figure 1. We can see that for some lines (e.g. Figure 1(a)) we should plot
extra pixels vertically, and for others (e.g. Figure 1(b)) we should plot them horizontally. Which
of these two approaches we use depends on the gradient m of the line. We identify the following
cases:

 If |m| ≤ 1 plot extra pixels vertically.


 If |m| > 1, plot extra pixels horizontally.
(a) (b)

Figure 1 - Increasing Line Width by Plotting Extra Pixels in the Vertical (a) and Horizontal
(b) Directions
Although this approach is simple and effective, there are two slight problems:

 The actual thickness of a plotted line depends on its slope. This is illustrated in Figure 2.
Although this is a small weakness of the technique most graphics packages do not
attempt to address this problem. You can notice this effect in, for example, the Paint
accessory in Microsoft Windows.
 The ends of lines are either vertical or horizontal. This is illustrated in Figure 3.
Depending on whether we are plotting extra pixels in the horizontal or vertical
directions, the line ends will be horizontal or vertical.

(a) (b)
Figure 2 - Line Thickness Depends on Its Gradient
Figure 3 – Horizontal/Vertical Line Ends
The answer to the second problem (horizontal/vertical line ends) is to use line caps. A line cap is
a shape that is applied to the end of the line only. Three types of line cap are common in
computer graphics:

 Butt cap
 Round cap
 Projecting square cap
These are illustrated in Figure 4. The butt cap is formed by drawing a line through each end-
point at an orientation of 90o to the direction of the line. The round cap is formed by drawing a
semi-circle at each end-point with radius equal to half the line width. The projecting square cap
is similar to the butt cap but the position of the line is extended by a distance of half the line
width.

Figure 4 - Types of Line Cap


These line caps effectively solve the problem of vertical/horizontal line endings, but in the
process they introduce a new problem. If we are drawing polylines, or a connected series of line
segments, with these line caps, we can get problems at line joins. For example, Figure 5 shows
what happens at the line join of two line segments drawn with butt caps. There is a small
triangular area at the join that does not get filled.
Figure 5 - Problems at Line Joins
This problem can be overcome by using the following join types:

 Miter join – extend the outer boundaries of the two lines until they meet.
 Round join – draw a circular boundary centred on the join point with a diameter equal to
the line width.
 Bevel join – use butt caps, then fill in the triangle that is left unfilled.
These join types are illustrated in Figure 6. One possible problem occurs when using the miter
join when the angle between the two lines is very small. In this case the projecting point of the
join can be very long, making the line-join appear unrealistic. Therefore graphics packages will
often check the angle between lines before using a miter join.

Figure 6 - Line Join Types

3.2. Line Style


The style of a line refers to whether it is plotted as a solid line, dotted, or dashed. The normal
approach to changing line style is to define a pixel mask. A pixel mask specifies a sequence of bit
values that determine whether pixels in the plotted line should be on or off. For example, the
pixel mask 11111000 means a dash length of 5 pixels followed by a spacing of 3 pixels. In other
words, if the bit in the pixel mask is a 1, we plot a pixel, and if it is a zero, we leave a space.

3.3. OpenGL Line Attribute Functions


OpenGL allows us to change both the line width and the line style. To change the line width we
use the glLineWidth routine. This takes a single floating point argument (which is rounded to the
nearest integer) indicating the thickness of the line in pixels. For example,

glLineWidth(4.3)

will set the line width state variable to 4 pixels. This value will be used for all subsequent line
primitives until the next call to glLineWidth. The ‘true’ line thickness will depend on the
orientation of the line.
The line style is specified using a pixel mask. First we must enable the line stipple feature of
OpenGL using the following line:

glEnable(GL_LINE_STIPPLE)

Next, we use the glLineStipple function to define the line style. glLineStipple takes two
arguments: a repeat factor, which specifies how many times each bit in the pixel mask should be
repeated, and a pattern, which is a 16-bit pixel mask. For example, the code shown below draws
the dashed line shown to the right. The pixel mask is the hexadecimal number 00FF, which is 8
zeros followed by 8 ones. The repeat factor is 1 so each one or zero in the pixel mask
corresponds to a single pixel in the line.

glEnable(GL_LINE_STIPPLE);
glLineWidth(3.0);
glLineStipple(1, 0x00FF);
glBegin(GL_LINE_STRIP);
glVertex2i(100,100);
glVertex2i(150,100);
glVertex2i(150,200);
glVertex2i(250,250);
glEnd();
glDisable(GL_LINE_STIPPLE);

Basic State Management


In the preceding section, you saw an example of a state variable, the current RGBA color, and how it can
be associated with a primitive. OpenGL maintains many states and state variables. An object may be
rendered with lighting, texturing, hidden surface removal, fog, and other states affecting its
appearance.

By default, most of these states are initially inactive. These states may be costly to activate; for
example, turning on texture mapping will almost certainly slow down the process of rendering a
primitive. However, the image will improve in quality and will look more realistic, owing to the
enhanced graphics capabilities.
To turn many of these states on and off, use these two simple commands:

Polygon Details

Polygons are typically drawn by filling in all the pixels enclosed within the boundary, but you can also
draw them as outlined polygons or simply as points at the vertices. A filled polygon might be solidly
filled or stippled with a certain pattern. Although the exact details are omitted here, filled polygons are
drawn in such a way that if adjacent polygons share an edge or vertex, the pixels making up the edge or
vertex are drawn exactly once—they’re included in only one of the polygons. This is done so that
partially transparent polygons don’t have their edges drawn twice, which would make those edges
appear darker (or brighter, depending on what color you’re drawing with). Note that it might result in
narrow polygons having no filled pixels in one or more rows or columns of pixels.

Polygons as Points, Outlines, or Solids


A polygon has two sides—front and back—and might be rendered differ- ently
depending on which side is facing the viewer. This allows you to have cutaway views of
solid objects in which there is an obvious distinction between the parts that are inside and
those that are outside. By default, both front and back faces are drawn in the same way. To
change this, or to draw only outlines or vertices, use glPolygonMode().

For example, you can have the front faces filled and the back faces outlined with two
calls to this routine:

glPolygonMode(GL_FRONT, GL_FILL);
glPolygonMode(GL_BACK, GL_LINE);

You might also like