CG Chapter 3
CG Chapter 3
Faculty of Technology
Department of Computer Science
CoSc 3072 – Computer Graphics
Chapter 3 – Graphics Primitives
All graphics packages construct pictures from basic
building blocks known as graphics primitives.
Geometric Primitives: Primitives that describe the
geometry, or shape, of these building blocks are
known as geometric primitives.
They can be anything from 2- D primitives such as
points, lines and polygons to more complex 3-D
primitives such as spheres and polyhedra (a
polyhedron is a 3-D surface made from a mesh of 2-D
polygons).
Fill Area Primitives: Refers to any enclosed boundary
that can be filled with a solid color or pattern.
02/22/2025
Character Primitives: Used to display text characters
con…
In the following sections we will examine some algorithms for
drawing different primitives
OpenGL Point Drawing Primitives
The most basic type of primitive is the point.
Many graphics packages, including OpenGL, provide
routines for displaying points.
To recap, we use the pair of functions glBegin … glEnd,
using the symbolic constant GL_POINTS to specify how the
vertices in between should be interpreted.
In addition, the function glPointSize can be used to set the
size of the points in pixels.
The default point size is 1 pixel. Points that have a size
greater than one pixel are drawn as squares with a side
length equal to their size.
02/22/2025
con…
For example, the following code draws three 2-D points with
a size of 2 pixels.
glPointSize(2.0);
glBegin(GL_POINTS);
glVertex2f(100.0, 200.0);
glVertex2f(150.0, 200.0);
glVertex2f(150.0, 250.0);
glEnd();
02/22/2025
Line Drawing Algorithms
Lines are a very common primitive and will be supported
by almost all graphics packages. In addition, lines form
the basis of more complex primitives such as polylines (a
connected sequence of straight-line segments) or
polygons (2-D objects formed by straight-line edges).
Lines are normally represented by the two end-points of
the line, and points (x,y) along the line must satisfy the
following slope-intercept equation:
* y = mx + c (1)
02/22/2025
Con….
*where m is the slope or gradient of the line, and c is
the coordinate at which the line intercepts the y-axis.
* Given two end-points (x0,y0) and (xend,yend), we
can calculate values for m and c as follows:
02/22/2025
DDA Line-Drawing Algorithm
02/22/2025
Con...
This would compute correct line points
but, as illustrated by Figure 1, it would
leave gaps in the line.
The reason for this is that the value of δy
is greater than one, so the gap between
subsequent points in the line is greater
than 1 pixel.
02/22/2025
Con….
Once we have computed values for δx and δy,
the basic DDA algorithm is:
*Start with (x ,y )
0 0
δx = 1
δy = 0.6
Using these values of δx and δy we can now start to plot line
points:
02/22/2025
Con….
*Start with (x ,y ) = (10,10) – colour this pixel
0 0
02/22/2025
Con..
The DDA algorithm is simple and easy to implement,
but it does involve floating point operations to calculate
each new point.
Floating point operations are time-consuming when
compared to integer operations.
Since line-drawing is a very common operation in
computer graphics, it would be nice if we could devise a
faster algorithm which uses integer operations only.
The next section describes such an algorithm.
02/22/2025
Bresenham’s Line-Drawing Algorithm
Bresenham’s line-drawing algorithm provides significant
improvements in efficiency over the DDA algorithm.
These improvements arise from the observation that for
any given line, if we know the previous pixel location, we
only have a choice of 2 locations for the next pixel.
This concept is illustrated in Figure 3: given that we know
(xk,yk) is a point on the line, we know the next line point
must be either pixel A or pixel B.
Therefore we do not need to compute the actual floating-
point location of the ‘true’ line point; we need only make a
decision between pixels A and B.
.
02/22/2025
Con…
02/22/2025
Con…..
02/22/2025
Con…..
*Now, we can decide which of pixels A and B to choose
based on comparing the values of dupper and dlower:
*If dlower > dupper, choose pixel A
*Otherwise choose pixel B
*We make this decision by first subtracting dupper from
dlower:
Decision variable pk=dlower-dupper
02/22/2025
Con….
02/22/2025
Con….
where d is a constant that has the value
2Dy + 2cDx - Dx .
Note that the sign of pk will be the same as
the sign of (dlower – dupper), so if pk is
positive we choose pixel A and if it is negative
we choose pixel B.
In addition, pk can be computed using only
integer calculations, making the algorithm
very fast compared to the DDA algorithm.
02/22/2025
Con….
An efficient incremental calculation makes Bresenham’s algorithm
even faster.
(An incremental calculation means we calculate the next value of
pk from the last one.)
Given that we know a value for pk, we can calculate pk+1 from
Eq. (10) by observing that:
02/22/2025
Con….
02/22/2025
Con….
02/22/2025
Con…..
*Exercise
*Consider the example of plotting the line shown in
Figure 2 using Bresenham’s algorithm:
02/22/2025
Con…
02/22/2025
Con….
02/22/2025
OpenGL Line Functions
We can draw straight-lines in OpenGL using the
same glBegin … glEnd functions that we saw for
point-drawing.
This time we specify that vertices should be
interpreted as line end-points by using the symbolic
constant GL_LINES. For example, the following code:
*glLineWidth(3.0);
*glBegin(GL_LINES);
*glVertex2f(100.0, 200.0);
*glVertex2f(150.0, 200.0);
*glVertex2f(150.0, 250.0);
*glVertex2f(200.0, 250.0);
*glEnd()
02/22/2025
Con…..
will draw two separate line segments: one from (100,200)
to (150,200) and one from (150,250) to (200,250).
The line will be drawn in the current drawing colour and
with a width defined by the argument of the function
glLineWidth.
Two other symbolic constants allow us to draw slightly
different types of straight-line primitive: GL_LINE_STRIP
and GL_LINE_LOOP.
The following example illustrates the difference between
the three types of line primitive.
First we define 5 points as arrays of 2
Glint values. Next,
we define exactly the same vertices for each of the three
types of line primitive.
The images to the right show how the vertices will be
interpreted by each primitive.
02/22/2025
Con…
*Glint p1[] = {200,100}; Glint p2[] = {50,0}
*Glint p3[] = {100,200}; Glint p4[] = {150,0}; Glint p5[]
= {0,100};
*glBegin(GL_LINES);
*glVertex2iv(p1);
*glVertex2iv(p2);
*glVertex2iv(p3);
*glVertex2iv(p4);
*glVertex2iv(p5);
*glEnd();
02/22/2025
Con….
*lBegin(GL_LINE_STRIP);
*glVertex2iv(p1);
*glVertex2iv(p2);
*glVertex2iv(p3);
*glVertex2iv(p4);
*glVertex2iv(p5);
*glEnd();
*glBegin(GL_LINE_LOOP);
*glVertex2iv(p1);
*glVertex2iv(p2);
*glVertex2iv(p3);
*glVertex2iv(p4);
*glVertex2iv(p5);
*glEnd(); 02/22/2025
Con…..
We can see that GL_LINES treats the vertices as
pairs of end-points.
Lines are drawn separately and any extra vertices
(i.e. a start-point with no end-point) are ignored.
GL_LINE_STRIP will create a connected polyline, in
which each vertex is joined to the one before it and
after it.
The first and last vertices are only joined to one
other vertex.
Finally, GL_LINE_LOOP is the same as GL_LINE_STRIP
except that the last point is joined to the first one to
create a loop.
02/22/2025
Circle-Drawing Algorithms
*Some graphics packages allow us to draw circle
primitives. Before we examine algorithms for circle-
drawing we will consider the mathematical equations of
a circle. In Cartesian coordinates we can write:
02/22/2025
Plotting Points Using Cartesian Coordinates
02/22/2025
Con…
This would correctly generate points on the boundary of
a circle.
However, like the first attempt at a line-drawing
algorithm we saw in Section 3.1 (see Figure 1) we would
end up with ‘holes’ in the line – see Figure 4.
We would have the same problem if we incremented
the y-coordinate and plotted a calculated x-coordinate.
As with the DDA line-drawing algorithm we can
overcome this problem by calculating and checking the
gradient: if |m| ≤ 1 then increment x and calculate y,
and if |m| > 1 then increment y and calculate x.
02/22/2025
Con….
02/22/2025
Plotting Points Using Polar Coordinates
*An alternative technique is to use the polar coordinate
equations.
*Recall that in polar coordinates we express a position
in the coordinate system as an angle θ and a distance
r.
* For a circle, the radius r will be constant, but we can
increment θ and compute the corresponding x and y
values according to Eqs. (15) and (16).
*For example, suppose we want to draw a circle with
(xc,yc) = (5,5) and r = 10. We start with θ = 0o and
compute x and y as:
*x = 5 + 10 cos 0o = 15
*y = 5 + 10 sin 0o = 5
*Therefore we plot (15,5) Next, we increase θ to 5o:
*x = 5 + 10 cos 5o = 14.96
*y = 5 + 10 sin 5o = 5.87 02/22/2025
*Therefore we plot (15,6)
Con……
This process would continue until we had plotted the
entire circle (i.e. θ = 360o).
Using this polar coordinate technique, we can avoid holes
in the boundary if we make small enough increases in the
value of θ.
This algorithm is more efficient than the Cartesian
plotting algorithm described in Section 4.1.
It can be made even more efficient, at a slight cost in
quality; by increasing the size of the steps in the value of
θ and then joining the computed points by straight- line
segments (see Figure 5).
02/22/2025
Con….
02/22/2025
Taking Advantage of the Symmetry of Circles in
Plotting
We can improve the efficiency of any circle-drawing
algorithm by taking advantage of the symmetry of
circles.
As illustrated in Figure 6, when we compute the
Cartesian coordinates x and y of points on a circle
boundary we have 4 axes of symmetry (shown in blue):
we can generate a point reflected in the y-axis by
negating the x-coordinate; we can generate a point
reflected in the x-axis by negating the y-coordinate, and
so on.
In total, for each point computed, we can generate
seven more through symmetry.
Computing these extra points just by switching or
negating the coordinates of a single point is much more
efficient than computing each boundary point
separately.
This 02/22/2025
means that we only need to compute boundary
Con……
02/22/2025
Midpoint Circle-Drawing Algorithm
02/22/2025
Con….
02/22/2025
Con…..
02/22/2025
Con……
02/22/2025
Con……
* pk =fcirc(xk+1, yk- 0.5) = (xk+1)2 + (yk- 0.5)2 - r2
* Pk=xk2+2xk+1+yk2-yk+1/4-r2 know we can calculate pknext=pk+1
*Pk next= xknext2+2xknext+1+yknext2-yknext+1/4-r2
* If pk<0
* pk+1-pk=(X +1)2+2x k k+1 +1+yk2-yk+1/4- 2- ((Xk+1) 2+yk2-yk+1/4-r2 )=
r
*pk+1-pk=2xk+1+1=>pk+1=pk+ 2x +1 k+1
*If pk>0
*yk+1=yk-1
*Pk+1-pk=(Xk+1)2+2x +1+(yk-1)2-(yk-1)+1/4-r2- ((Xk+1) 2+yk2-yk+1/4-r2 )
k+1
* p0 =fcirc(1, r - 0.5) = 1 + (r - 0.5)2 - r2 = 5 - r to find the value of p0 substitute (0,r) in the circle midpoint
formula 02/22/2025
Con…..
02/22/2025
Con…..
02/22/2025
*Fill-Area
Primitives
The most common type of primitive in 3-D computer
graphics is the fill-area primitive.
The term fill-area primitive refers to any enclosed
boundary that can be filled with a solid color or pattern.
However, fill-area primitives are normally polygons, as
they can be filled more efficiently by graphics packages.
Polygons are 2-D shapes whose boundary is formed by any
number of connected straight-line segments.
They can be defined by three or more coplanar vertices
(coplanar points are positioned on the same plane).
Each pair of adjacent vertices is connected in sequence by
edges.
Normally polygons should have no edge crossings: in this
case they are known as simple polygons or standard
polygons (see Figure 8).
02/22/2025
Con…..
02/22/2025
Convex and Concave Polygons
a) b)
Figure 10 – Types of Polygon: (a) Convex; (b)
Concave
02/22/2025
Polygon Inside-Outside Tests
02/22/2025
Con…
02/22/2025
Nonzero Winding Number Rule
*we can see from Figure 11(b) that the nonzero winding
number rule gives a slightly different result from the odd-
even rule for the example polygon given.
*In fact, for most polygons (including all convex polygons)
the two algorithms give the same result.
*But for more complex polygons such as that shown in
Figure 11 the nonzero winding number rule allows the
programmer a bit more flexibility, since they can control
the order of the edge vectors to get the results they want.
02/22/2025
Con….
Figure below shows a simple example of a geometric
table. We can see that there are three tables: a vertex
table, an edge table and a surface-facet table.
The edge table has pointers into the vertex table to
indicate which vertices comprise the edge.
Similarly the surface-facet table has pointers into the
edge table.
This is a compact representation for a polygonal mesh,
because each vertex’s coordinates are only stored once, in
the vertex table, and also information about each edge is
only stored once.
*
02/22/2025
OpenGL Fill-Area Routines
OpenGL provides a variety of routines to draw fill-area
polygons. In all cases these polygons must be convex.
In most cases the vertices should be specified in an anti-
clockwise direction when viewing the polygon from outside
the object, i.e. if you want the front-face to point towards
you.
The default fill-style is solid, in a color determined by the
current color settings.
In all, there are seven different ways to draw polygons in
OpenGL:
*glRect*
Six different symbolic constants for use with glBegin …
glEnd
We will now consider each of these techniques in turn.
*glRect*
02/22/2025
Con….
Two-dimensional rectangles can also be drawn using some
of the other techniques described below, but because
drawing rectangles in 2-D is a common task OpenGL
provides the glRect* routine especially for this purpose
(glRect* is more efficient for 2-D graphics than the other
alternatives).
The basic format of the routine is:
*glRect* (x1, y1, x2, y2)
*where (x1,y1) and (x2,y2) define opposite corners of the
rectangle. Actually when we call the glRect* routine,
OpenGL will construct a polygon with vertices defined in
the following order:
*(x1,y1), (x2,y1), (x2,y2), (x1,y2)
*For example, Figure 12 shows an example of executing the
following call to glRecti:
*glRecti(200,100,50,250); 02/22/2025
Con….
02/22/2025
Con….
* GL_POLYGON
*The GL_POLYGON symbolic constant defines a single convex
polygon. Like all of the following techniques for drawing fill-
area primitives it should be used as the argument to the
glBegin routine. For example, the code shown below will draw
the shape shown in Figure 13. Notice that the vertices of the
*polygon are specified in anti-clockwise order.
* glBegin(GL_POLYGON);
* glVertex2iv(p1);
* glVertex2iv(p2);
* glVertex2iv(p3);
* glVertex2iv(p4);
* glVertex2iv(p5); Figure 13 - A Polygon Drawn Using
the GL_POLYGON OpenGL Primitive
* glVertex2iv(p6);
* glEnd();
02/22/2025
Con…..
*GL_TRIANGLES
*The GL_TRIANGLES symbolic constant causes the glBegin …
glEnd pair to treat the vertex list as groups of three 3
vertices, each of which defines a triangle.
* The vertices of each triangle must be specified in anti-
clockwise order. Figure 14 illustrates the use of the
GL_TRIANGLES primitive.
*glBegin(GL_TRIANGLES);
*glVertex2iv(p1);
* glVertex2iv(p2);
* glVertex2iv(p6);
* glVertex2iv(p3);
*glVertex2iv(p4);
*glVertex2iv(p5);
*glEnd(); 02/22/2025
Con..
GL_TRIANGLE_STRIP
*To form polygonal meshes it is often convenient to define a
number of triangles using a single glBegin … glEnd pair. The
GL_TRIANGLE_STRIP primitive enables us to define a strip of
connected triangles. The vertices of the first triangle only must
be specified in anti-clockwise order. Figure 15 illustrates the
use
*of GL_TRIANGLE_STRIP.
*glBegin(GL_TRIANGLE_STRIP);
*glVertex2iv(p1);
*glVertex2iv(p2);
*glVertex2iv(p6);
* glVertex2iv(p3);
*glVertex2iv(p5);
*glVertex2iv(p4);
*glEnd(); 02/22/2025
Con…
* GL_TRIANGLE_FAN
GL_TRIANGLE_FAN is similar to
GL_TRIANGLE_STRIP, in that it allows us to define
a number of triangles using a single glBegin …
glEnd command.
In this case, we can define a ‘fan’ of triangles
emanating from a single point.
The first point of the first triangle is the source of
the fan, and the vertices of this triangle must be
specified in an anti-clockwise direction.
In addition, the fan of triangles must also be
defined in an anti-clockwise direction. See Figure
below for an example of the use of
GL_TRIANGLE_FAN.
02/22/2025
Con…
* glBegin(GL_TRIANGLE_FAN);
* glVertex2iv(p1);
* glVertex2iv(p2);
* glVertex2iv(p3);
* glVertex2iv(p4);
* glVertex2iv(p5);
* glVertex2iv(p6);
* glEnd();
GL_QUADS
* Using the GL_QUADS primitive, the vertex list is treated as
groups of four vertices, each of which forms a quadrilateral.
* If the number of vertices specified is not a multiple of four,
then the extra vertices are ignored.
* The vertices for each quadrilateral must be defined in an anti-
clockwise direction.
02/22/2025
Con…
*glBegin(GL_QUADS);
*glVertex2iv(p1);
*glVertex2iv(p2);
*glVertex2iv(p3);
*glVertex2iv(p4);
* glVertex2iv(p5);
*glVertex2iv(p6);
*glVertex2iv(p7);
* glVertex2iv(p8);
*glEnd();
02/22/2025
Con…
*GL_QUAD_STRIP
*In the same way that GL_TRIANGLE_STRIP allowed us to
define a strip of connected triangles, GL_QUAD_STRIP
allows us to define a strip of quadrilaterals. The first four
vertices form the first quadrilateral, and each subsequent
pair of vertices is combined with the two before them to
form another quadrilateral. The vertices of the first
quadrilateral must be specified in an anti-clockwise
direction. Figure 18 illustrates the use of GL_QUAD_STRIP.
02/22/2025
Con…
*glBegin(GL_QUAD_STRIP);
*glVertex2iv(p1);
*glVertex2iv(p2);
*glVertex2iv(p3);
*glVertex2iv(p4);
*glVertex2iv(p5);
*glVertex2iv(p6);
*glVertex2iv(p7);
*glVertex2iv(p8);
*glEnd();
02/22/2025
Character Primitives
The final type of graphics primitive we will consider is the
character primitive.
Character primitives can be used to display text characters.
Before we examine how to display characters in OpenGL, let us
consider some basic concepts about text characters.
We can identify two different types of representation for
characters: bitmap and stroke (or outline) representations.
Using a bitmap representation (or font), characters are stored
as a grid of pixel values (see Figure 19(a)).
This is a simple representation that allows fast rendering of the
character.
However, such representations are not easily scalable: if we
want to draw a larger version of a character defined using a
bitmap we will get an aliasing effect (the edges of the
characters will appear jagged due to the low resolution).
Similarly, we cannot easily generate bold or italic versions of
the character.
02/22/2025
Con…
An alternative representation to bitmaps is the stroke, or
outline, representation (see Figure 19(b)).
Using stroke representations characters are stored using
line or curve primitives.
To draw the character we must convert these primitives
into pixel values on the display.
As such, they are much more easily scalable: to generate
a larger version of the character we just multiply the
coordinates of the line/curve primitives by some scaling
factor.
Bold and italic characters can be generated using a
similar approach.
The disadvantage of stroke fonts is that they take longer
time to draw than bitmap fonts.
02/22/2025
Character Primitives
*Finally, we can categorise fonts as either monospace or
proportional fonts.
*Characters drawn using a monospace font will always take
up the same width on the display, regardless of which
character is being drawn.
*With proportional fonts, the width used on the display will
be proportional to the actual width of the character, e.g.
the letter ‘i’ will take up less width than the letter ‘w’.
*As shown below, Courier is an example of a monospace
font whereas Times-Roman and Arial are examples of
proportional fonts:
*Courier is a monospace font
*Times-Roman is a proportional font
02/22/2025
Character Primitives
02/22/2025
* OpenGL Character Primitive
*OpenGL on its own does not contain any routines
Routines
dedicated to drawing text characters.
* However, the glut library does contain two different
routines for drawing individual characters (not strings).
*Before drawing any character, we must first set the raster
position, i.e. where will the character be drawn. We need
to do this only once for each sequence of characters.
*After each character is drawn the raster position will be
automatically updated ready for drawing the next
character.
* To set the raster position we use the glRasterPos2i
routine. For example,
* glRasterPos2i(x, y)
* positions the raster at coordinate location (x,y).
02/22/2025
Con….
*Next, we can display our characters.
*The routine we use to do this will depend on whether we
want to draw a bitmap or stroke character. For bitmap
characters we can write, for example,
*glutBitmapCharacter(GLUT_BITMAP_9_BY_15, ‘a’);
*This will draw the character ‘a’ in a monospace bitmap
font with width 9 and height 15 pixels.
*There are a number of alternative symbolic constants that
we can use in place of GLUT_BITMAP_9_BY_15 to specify
different types of bitmap font. For example,
*GLUT_BITMAP_8_BY_13
*GLUT_BITMAP_9_BY_15
02/22/2025
Con….
*We can specify proportional bitmap fonts using the
following symbolic constants:
*GLUT_BITMAP_TIMES_ROMAN_10
*GLUT_BITMAP_HELVETICA_10
*Here, the number represents the height of the font.
* Alternatively, we can use stroke fonts using the
glutStrokeCharacter routine.
* For example,glutStrokeCharacter(GLUT_STROKE_ROMAN,
‘a’);
*This will draw the letter ‘a’ using the Roman stroke font,
which is a proportional font.
*We can specify a monospace stroke font using the
following symbolic constant:
*GLUT_STROKE_MONO_ROMAN
02/22/2025
Con….
Thank You!!!!!!!!!!
02/22/2025