0% found this document useful (0 votes)
6 views

All Basics of Computer Graphics

The document provides an overview of computer graphics, emphasizing its goal to create realistic and useful images for various applications, including data visualization, virtual reality, and entertainment. It discusses the evolution of computer graphics, starting from early programs like Sketchpad to modern applications in CAD, scientific visualization, and user interfaces. Additionally, it categorizes graphics software into special-purpose and general-purpose packages, highlighting their roles in generating and manipulating visual content.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views

All Basics of Computer Graphics

The document provides an overview of computer graphics, emphasizing its goal to create realistic and useful images for various applications, including data visualization, virtual reality, and entertainment. It discusses the evolution of computer graphics, starting from early programs like Sketchpad to modern applications in CAD, scientific visualization, and user interfaces. Additionally, it categorizes graphics software into special-purpose and general-purpose packages, highlighting their roles in generating and manipulating visual content.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 86

Department of Computer Science

Computer Graphics
Part I:- Introduction to Computer Graphics
The aim of computer graphics is to produce realistic and/or useful images on a computer. For some
applications the emphasis will be on realism (e.g. special effects in films), whereas for others it
will simply be on usefulness (e.g. data visualisation). We will discuss some different applications
of computer graphics in Section 4.
Computer graphics provides methods to generate images using a computer. The word “image”
should be understood in a more abstract sense here. An image can represent a realistic scene from
the real world, but graphics like histograms (statics diagram) or pie charts as well as the graphical
user interface of a software tool are also considered as images. The development of computer
graphics has made computers easier to interact with, and better for understanding and interpreting
many types of data. Developments in computer graphics have had a profound impact on many
types of media and have revolutionized animation, movies and the video game industry.

The term computer graphics has been used in a broad sense to describe "almost everything on
computers that is not text or sound". Typically, the term computer graphics refers to several
different things:

 the representation and manipulation of image data by a computer


 the various technologies used to create and manipulate images
 the images so produced, and
 the sub-field of computer science which studies methods for digitally synthesizing and
manipulating visual content

Today, computers and computer-generated images touch many aspects of daily life. Computer
imagery is found on television, in newspapers, for example in weather reports, or for example in
all kinds of medical investigation and surgical procedures. A well-constructed graph can present
complex statistics in a form that is easier to understand and interpret. In the media "such graphs
are used to illustrate papers, reports, thesis", and other presentation material.

Many powerful tools have been developed to visualize data. Computer generated imagery can be
categorized into several different types: 2D, 3D, 4D, 7D, and animated graphics. As technology
has improved, 3D computer graphics have become more common, but 2D computer graphics are
still widely used. Computer graphics has emerged as a sub-field of computer science which studies

KC Instr.H.Kedir Dept of cs Page 1


methods for digitally synthesizing and manipulating visual content. Over the past decade, other
specialized fields have been developed like information visualization, and scientific visualization
more concerned with "the visualization of three dimensional phenomena (architectural,
meteorological, medical, biological, etc.), where the emphasis is on realistic renderings of
volumes, surfaces, illumination sources, and so forth, perhaps with a dynamic (time) component".

Whatever the application, the final image will be generated from some model of the scene we want
to take a picture of. For example, in Figure 1 we have a representation of a 3-D tetrahedron (the
object model), together with some model of how the scene is lit (the light sources), and we want
to produce an image on the image plane based on the position of the viewer. The object model will
typically represent the geometry of the object together with some information about what type of
material it is made of (i.e. what it looks like). Normally we consider the viewer to be a virtual
camera, which defines how the picture is taken. This process of starting with a model (often a 3-
D model) of the world and taking a picture of it with a virtual camera is known in computer
graphics as rendering. The rendering process can include many stages, such as hidden surface
elimination, surface rendering and clipping, which we will deal with throughout this course.

Figure 1 - Rendering a 2-D Image from a 3-D Model

Some graphics applications can be described as interactive graphics applications. In these cases,
images need to be generated in real-time and as well as viewing the images the user can interact
with the model of the world using specialised input hardware. For example, a mouse, keyboard,
tablet and stylus, scanner or a virtual reality headset and gloves can all be used as interactive
graphics input devices.

1. Initial Development of Computer Graphics

The advance in computer graphics was to come from one MIT (Massachusetts Institute of
Technology: an engineering university in Cambridge) student, Ivan Sutherland. In 1961 Sutherland
created another computer drawing program called Sketchpad. Using a light pen, Sketchpad
allowed one to draw simple shapes on the computer screen, save them and even recall them later.

KC Instr.H.Kedir Dept of cs Page 2


The light pen itself had a small photoelectric cell in its tip. This cell emitted an electronic pulse
whenever it was placed in front of a computer screen and the screen's electron gun fired directly
at it. By simply timing the electronic pulse with the current location of the electron gun, it was
easy to pinpoint exactly where the pen was on the screen at any given moment. Once that was
determined, the computer could then draw a cursor at that location. Even today, many standards
of computer graphics interfaces got their start with this early Sketchpad program. One example of
this is in drawing constraints. If one wants to draw a square for example, s/he doesn't have to worry
about drawing four lines perfectly to form the edges of the box. One can simply specify that s/he
wants to draw a box, and then specify the location and size of the box. The software will then
construct a perfect box, with the right dimensions and at the right location.

2. Applications of Computer Graphics

Before we look in any more detail at how computers can generate graphical images, let us consider
why we would want to do this. Since the early days of computing, the field of computer graphics
has become a very popular one because of its wide range of applications. The following sections
summarise the main categories of application.

2.1. Graphs and Charts

Graphs and charts have long been used for visualising data. They are particularly widely used for
visualising relationships and trends in scientific, mathematical, financial and economic data.

Although, in principle, we do not need advanced graphics algorithms to display graphs and charts,
many modern visualisation packages use 3-D effects such as shadowing to make the graph/chart
more visually appealing. For example, Figure 2 shows a range of graphs and charts produced by
the free FusionCharts Lite package (http://www.infosoftglobal.com/FusionCharts/Lite).
Commercial packages are also available and include Microsoft Excel.

(a) (b)

KC Instr.H.Kedir Dept of cs Page 3


(c) (d)

Figure 2 - Computer Graphics used for Graphs and Charts

2.2. Computer-Aided Design

Computer-Aided Design (CAD) and Computer-Aided Drafting and Design (CADD) involve using
a computer application to enable designers to construct and visualise 3-D models. They are
commonly used in fields such as architecture, engineering and circuit design. For example, Figure
3 shows two screenshots from CAD applications being used for architecture. One of the most
common CAD applications is AutoCAD.

Computer-Aided Manufacturing (CAM) is a similar concept to CAD, except that the design
application is linked to the manufacturing process, i.e. the application will directly control, via a
hardware communication link, the machine that manufactures the object being designed.

(a) (b)

Figure 3 - Computer Graphics used for Computer-Aided Design (CAD)

KC Instr.H.Kedir Dept of cs Page 4


2.3. Data Visualisation

We already saw in Section 2.1 how graphics can be used for visualising data in the form of graphs
and charts. More complex, often multidimensional, datasets can also be visualised using graphics
techniques. In this case, it is often difficult to visualise such datasets without computer graphics.
These complex datasets are particularly common in science (scientific visualisation) and business
(business visualisation).

For example, Figure 4(a) shows a three-dimensional medical dataset of a patient. Here we use
computer graphics to visualise the paths of blood vessels inside the patient’s head. Figure 4(b)
shows an image generated from data acquired by the Hubble Space Telescope. This image of a
distant galaxy is allowing us to visualise a four-dimensional dataset – three dimensions are
combined as the red, green and blue components of an optical image, and the fourth (an X-ray
image) is displayed as overlaid contour plots. Figure 4(c) shows another medical dataset, this time
acquired using a Magnetic Resonance Imaging (MRI) scanner. Computer graphics allow us to
visualise this three-dimensional dataset as three slices through the 3-D volume. Finally, Figure
4(d) shows the use of colour to visualise altitude in a map of the United States.

(a) (b)

KC Instr.H.Kedir Dept of cs Page 5


(c) (d)

Figure 4 - Computer Graphics used for Visualisation of Complex Datasets

2.4. Virtual Reality and Augmented Reality

Virtual Reality (VR) allows users to be immersed in a computer generated world, and to interact
with it as if they were interacting with the real world. Typically, the user will wear special hardware
such as the VR-headset shown in Figure 5(a). This allows the computer to completely control the
visual information received by the user, so if realistic computer-generated images such as that
shown in Figure 5(b) are displayed in the headset in stereo (i.e. different images for the left and
right eyes) the user will have a sense of physical immersion in the virtual world. In addition, other
specialised hardware such as VR-gloves can be used to interact with the ‘objects’ in the virtual
world.

An alternative way of experiencing virtual reality is by using a CAVE (which stands for Cave
Automatic Virtual Environment). An example of this is shown in Figure 5(c). Here, the user stands
inside a cubicle which has images projected onto the walls and ceiling. Often there will also be a
means of ‘moving’ in the virtual world such as pressure pads.

Augmented Reality (AR) combines a computer-generated virtual world with the real world. In AR,
computer-generated images are overlaid onto a user’s view of the real world. For example, Figure
5(d) shows an AR system used for surgery, in which computer-generated images of hidden features
such as blood vessels and tumours are overlaid on the surgeon’s view of the patient through a
surgical microscope.

KC Instr.H.Kedir Dept of cs Page 6


(a) (b)

(c) (d)

Figure 5 - Computer Graphics used for Virtual Reality and Augmented Reality

2.5. Education and Training

Computer graphics can be very beneficial in education and training. For example, Figure 6(a)
shows a screenshot from some educational software that teaches students about human anatomy.
Graphics are used to help students visualise the appearance and location of different organs inside
the body.

Another common application of computer graphics is in training. In many jobs it is difficult for
workers to get direct experience of the environment that they are expected to work in. This may
be because the environment is inaccessible (e.g. space), dangerous (e.g. bomb disposal), expensive
(e.g. flying aircraft) or high-risk (e.g. surgery). Computer graphics, combined with specialised
hardware, can be used to simulate the working environment, so that workers can gain the skills
required for their work. For example, Figure 6(b) shows a picture of a truck-driving simulator used

KC Instr.H.Kedir Dept of cs Page 7


by the German army to train their drivers in emergency situations; Figure 6(c) shows a helicopter
simulator; and Figure 6(d) shows a screenshot from a simulator for laser eye surgery.

(a) (b)

(c) (d)

Figure 6 - Computer Graphics used for Education and Training

2.6. Graphical User Interfaces

Another possibility that has been opened up by the improvements in computer graphics technology
is in the area of user interfaces. Figure 7 shows the difference in visual appeal between an old text-
based user interface and a modern windows-based user interface. And the difference is not just in
visual appeal: modern user interfaces have made multitasking (doing several things at once) much
easier and applications such as word-processors have become much more powerful.

KC Instr.H.Kedir Dept of cs Page 8


(a) (b)

Figure 7 - Text-Based and Graphical User Interfaces

2.7. Entertainment

The applications of computer graphics in entertainment fall into three categories: computer art,
special effects and animations, and games.

A number of artists have taken advantage of the possibilities offered by computer graphics in
producing works of art. For example, Figure 8(a) shows a frame from an animated artwork by
William Latham.

Computer-generated images (CGI) have been widely used in the entertainment industry for
producing special effects for films, and also for producing completely computer-generated films.
Figure 8(b) shows a frame from the film Final Fantasy: the Spirits Within, which featured 100%
computer-generated images. For applications such as this, highly realistic images can be produced
off-line, i.e. we do not need to generate the images in real-time, they need only be generated once,
frame-by-frame, and then combined into the desired animated sequence.

For computer games, images must be generated in real-time in response to user actions. Therefore
the graphics in games can be less realistic, since the emphasis is on speed of generation rather than
realism. Figure 8(c) and (d) show screenshots from the popular computer game Doom.

KC Instr.H.Kedir Dept of cs Page 9


(a) (b)

(c) (d)

Figure 8 - Computer Graphics used in the Entertainment Industry

3. Graphics Software

To generate graphical images like those we have seen above, we need some type of graphics
software. We can divide graphics software into two categories:
 Special-purpose graphics packages
 General-purpose graphics packages

Special-purpose packages are designed for a specific purpose. For example a drawing package
such as the Paint accessory in Microsoft Windows is an example of a special-purpose graphics
package: it is designed to allow users to draw simple pictures consisting of lines, curves and other
basic components. Another example is CAD packages such as AutoCAD: these are designed to
allow users to build and visualise 3-D models. Special-purpose graphics packages tend to be used
mostly by end-users, not programmers or computer professionals. In other words, you do not need

KC Instr.H.Kedir Dept of cs Page 10


to know about computer graphics theory to use them. Other examples of special-purpose packages
are the drawing applications Adobe Photoshop, Xara and GIMP; the 3-D modelling applications
3-D Studio Max and Design Works; and the visualisation software FusionCharts and Microsoft
Excel.

General-purpose packages are a pre-defined library of routines for drawing graphics primitives.
They are used by computer programmers to add graphical capability to their programs. They
provide a lot more flexibility than special-purpose packages but they require technical knowledge
of computer graphics and programming and so are not suitable for end-users. Examples of general-
purpose graphics packages are the standard low-level graphics library OpenGL, the Microsoft
equivalent DirectX and the higher-level library OpenInventor. We can also think of scene
description languages as a type of general-purpose graphics package. Scene description languages
are languages that can be used to define 3-D models of scenes. Special viewing software is required
to view and often interact with the scene. The most common example of a scene description
language is VRML, the Virtual Reality Modelling Language.

4. Graphics Display Devices


Any computer-generated image must be displayed in some form. The most common graphics
display device is the video monitor, and the most common technology for video monitors is the
Cathode Ray Tube (CRT). Now we will examine the technology behind CRT displays, and how
they can be used to display images.

4.1. CRT Displays


CRT display devices are very common in everyday life as well as in computer graphics: most
television sets use CRT technology. Figure 9 illustrates the basic operation of a CRT display.
Beams of electrons are generated by electron guns and fired at a screen consisting of thousands of
tiny phosphor dots. When a beam hits a phosphor dot it emits light with brightness proportional to
the strength of the beam. Therefore pictures can be drawn on the display by directing the electron
beam to particular parts of the screen. The beam is directed to different parts of the screen by
passing it through a magnetic field. The strength and direction of this field, generated by the
deflection yoke, determines the degree of deflection of the beam on its way to the screen.

To achieve a colour display, CRT devices have three electron beams, and on the screen the
phosphor dots are in groups of three, which give off red, green and blue light respectively. Because
the three dots are very close together the light given off by the phosphor dots is combined, and the
relative brightnesses of the red, green and blue components determines the colour of light
perceived at that point in the display.

KC Instr.H.Kedir Dept of cs Page 11


Figure 9 - A Cathode Ray Tube Display Device

4.2. Random Scan Devices


CRT displays can work in one of two ways: random scan devices or raster scan devices. In a
random scan device the CRT beam is only directed to areas of the screen where parts of the picture
are to be drawn. If a part of the screen is blank the electron beam will never be directed at it. In
this case, we draw a picture as a set of primitives, for example lines or curves (see Figure 10). For
this reason, random scan devices are also known as vector graphics displays.

Random scan displays are not so common for CRT devices, although some early video games did
use random scan technology. These days random scan is only really used by some hard-copy
plotters.

4.3. Raster Scan Devices


In a raster scan device the primitives to be drawn are first converted into a grid of dots (see Figure
10). The brightness’s of these dots are stored in a data structure known as a frame buffer. The CRT
electron beam then sweeps across the screen line-by-line, visiting every location on the screen, but
it is only switched on when the frame buffer indicates that a dot is to be displayed at that location.

Raster scan CRT devices are the most common type of graphics display device in use today,
although recently LCD (Liquid Crystal Display) devices have been growing in popularity. In this
course, though, we will concentrate on the details of raster scan devices.

KC Instr.H.Kedir Dept of cs Page 12


Figure 10 - Random Scan and Raster Scan CRT Displays
Before we proceed, we must introduce some important terminology used when discussing raster
scan devices (see Figure 11). Whilst the frame buffer stores the image data ready for display, the
actual display itself is known as the raster. The raster is a grid of phosphor dots on the display
screen. Each of these dots is known as a picture cell, or pixel for short. Each row of pixels in the
raster is known as a scan-line. The number of pixels in a scan-line is known as the x-resolution of
the raster, and the number of scan-lines is known as the y-resolution. Finally, the ratio of the y-
resolution to the x-resolution (or sometimes the other way around) is known as the aspect ratio of
the display.

Figure 11 - Raster Scan Terminology

KC Instr.H.Kedir Dept of cs Page 13


We mentioned in Section 4.1 that for colour CRT displays there are three electron beams: one for
red light, one for green light and one for blue light. To ensure that these three beams precisely hit
the appropriate phosphor dots on the display, after the beams have been deflected by the deflection
yoke they pass through a mask containing many tiny holes – one hole for each pixel in the raster
(see Figure 9 and Figure 12). This mask is known as the shadow mask. Because the three electron
beams originated from slightly different locations (from three separate electron guns), if they all
pass through the same hole in the shadow mask they will impact the screen at slightly different
locations: the locations of the red, green and blue phosphor dots.

Figure 12 - The Shadow Mask in a Raster Scan CRT Display


The phosphor dots on a CRT display device only emit light for a very brief period of time after the
electron beam has moved on (the length of time that the phosphor emits light is known as its
persistence). Therefore to give the impression of a permanent image on the screen the raster must
be continually updated. Raster scan systems perform this continual update by ‘sweeping’ the
electron beams across the raster scan-line by scan-line, starting from the top and working towards
the bottom. When the last scan-line is completed we start again from the top.

The number of times that the entire raster is refreshed (i.e. drawn) each second is known as the
refresh rate of the device. For the display to appear persistent and not to flicker the display must
update often enough so that we cannot perceive a gap between frames. In other words, we must
refresh the raster when the persistence of the phosphor dots is beginning to wear off. In practise,
if the refresh rate is more than 24 frames per second (f/s) the display will appear reasonably smooth
and persistent.

KC Instr.H.Kedir Dept of cs Page 14


Modern graphics displays have high refresh rates, typically in the region of 60 f/s. However, early
graphics systems tended to have refresh rates of about 30 f/s. Consequently, some flicker was
noticeable. To overcome this, a technique known as interlaced scanning was employed. Using
interlaced scanning alternate scan-lines are updated in each raster refresh. For example, in the first
refresh only odd numbered scan-lines may be refreshed, then on the next refresh only even-
numbered scan-lines, and so on (see Figure 13). Because this technique effectively doubles the
screen refresh rate, it has the effect of reducing flicker for displays with low refresh rates.
Interlaced scanning was common in the early days of computer graphics, but these days displays
have better refresh rates so it is not so common.

The following are the specifications of some common video formats that have been (and still are)
used in computer graphics:
 VGA: resolution 640x480, 60 f/s refresh rate, non-interlaced scanning.
 PAL: resolution 625x480, 25 f/s refresh rate, interlaced scanning
 NTSC: resolution 525x480, 30 f/s refresh rate, interlaced scanning

Figure 13 - Interlaced Scanning for Raster Scan Displays

4.3.1. Frame Buffers


In Section 4.3 we introduced the concept of a frame buffer. Frame buffers are used by raster scan
display devices to store the pixel values of the image that will be displayed on the raster. It is a 2-
D array of data values, with each data value corresponding to a pixel in the image. The number of
bits used to store the value for each pixel is known as the bit-planes or depth of the frame buffer.
For example, a 640x480x8 frame buffer has a resolution of 640x480 and a depth of 8 bits; a
1280x1024x24 frame buffer has a resolution of 1280x1024 and a depth of 24 bits. For colour
displays we need to store a value for each component of the colour (red, green and blue), so the
bit-planes will typically be a multiple of 3 (e.g. 8 bit-planes each for red, green and blue makes a
total of 24 bit-planes)

KC Instr.H.Kedir Dept of cs Page 15


4.3.2. Architecture of Raster Graphics Systems
Now we are in a position to examine the basic architecture of raster graphics systems, i.e. what
components are required, and how do they interact? Figure 14 shows a block-diagram of a typical
raster graphics system. Most (non-graphics) processing will occur in the CPU of the computer,
which uses the system bus to communicate with the system memory and peripheral devices. When
graphics routines are to be executed, instead of being executed by the CPU they are passed straight
to the display processor, which contains dedicated hardware for drawing graphics primitives. The
display processor writes the image data into the frame buffer, which is a reserved portion of the
display processor memory. Finally the video controller reads the data in the frame buffer and uses
it to control the electron beams in the CRT display.

The display processor is also known by a variety of other names: graphics controller, display
coprocessor, graphics accelerator and video card are all terms used to refer to the display
processor. Since the display processor contains dedicated hardware for executing graphics routines
it must be dedicated to a particular set of routines. In other words, display processors will only be
able to handle the graphics processing if the routines used are from a particular graphics package.
This is known as hardware rendering. Most commercial video cards will support hardware
rendering for the OpenGL graphics package, and many PC video cards will also support hardware
rendering for DirectX. Hardware rendering is much faster than the alternative, software rendering,
in which graphics routines are compiled and executed by the CPU just like any other code. For the
raster graphics architecture to support software rendering the block-diagram shown in Figure 14
would need to be modified so that the frame buffer was connected directly to the system bus in
order that it could be updated by the CPU.

Figure 14 - The Architecture of a Raster Graphics System with a Display Processor

KC Instr.H.Kedir Dept of cs Page 16


4.4. 3-D Display Devices

In recent years the popularity of 3-D graphics display devices has been growing, although currently
they are still quite expensive compared with traditional 2-D displays. The aim of 3-D display
devices is to provide a stereo pair of images, one to each eye of the viewer, so that the viewer can
perceive the depth of objects in the scene as well as their position. The process of generating such
3-D displays is known as stereoscopy.

3-D displays can be divided into two types: head-mounted displays (HMDs) and head-tracked
displays (HTDs). HMDs are displays that are mounted on the head of the viewer. For example,
Figure 15 shows a HMD – the device fits on the head of the user and displays separate images to
the left and right eyes, producing a sense of stereo immersion. Such devices are common in virtual
reality applications. Normally a tracking device will be used to track the location of the display
device so that the images presented to the user can be updated accordingly – giving the impression
that the viewer is able to ‘move’ through the virtual world.

Whereas with a HMD the display moves with the viewers head, with a HTD the display remains
stationary, but the head of the viewer is tracked so that the images presented in the display can be
updated. The difficulty with HTDs is how to ensure that the left and right eyes of the viewer receive
separate stereo images to give a 3-D depth effect. A number of technologies exist to achieve this
aim. For example, the display can use polarised light filters to give off alternate vertically and
horizontally polarised images; the viewer then wears special glasses with polarised filters to ensure
that, for example, the left eye receives the vertically polarised images and the right eye receives
the horizontally polarised images. An alternative technique is to use colour filters: the display
draws a left-eye image in, for example, blue, and a right eye image in green; then the viewer wears
glasses with colour filters to achieve the stereo effect. Other technologies also exist.

Figure 15 - A Virtual Reality Headset

5. OpenGL

Now we will introduce the OpenGL graphics package. This part of the course is aimed to give you
practical experience to reinforce the theory behind computer graphics. Code examples will be

KC Instr.H.Kedir Dept of cs Page 17


given in C++, and you are expected to experiment with the OpenGL routines that are introduced
throughout the course.

In this course we will be using OpenGL together with the GL Utilities Toolkit (glut). You should
make sure that you have access to a C++ development environment that supports these two
libraries. Note that Turbo C++ does not support either of them. Two possibilities are:
 The free Dev-C++ environment (www.bloodshed.net) has OpenGL built-in and glut can
be easily added on as a separate package.
 Microsoft Visual C++ also has OpenGL built-in but not glut. The glut library is available
as a free download from http://www.xmission.com/~nate/glut.html. Installation is fairly
straightforward.
The examples and exercise solutions given in this course were coded with Dev-C++, but the same
source code should compile with Visual C++. Also, if you use Dev-C++, it comes with a very
useful OpenGL help file that you can use as a reference for different OpenGL routines (it will be
found in the Docs/OpenGL folder inside the Dev-C++ installation folder).

5.1. Introduction
OpenGL is based on the GL graphics package developed by the graphics hardware manufacturer
Silicon Graphics. GL was a popular package but it was specific to Silicon Graphics systems, i.e.
code written using GL would only run on Silicon Graphics hardware. In an attempt to overcome
this limitation, OpenGL was developed in the early 1990’s as a free platform-independent version
of GL. The package was developed, and is still maintained, by a consortium of graphics companies.

Although OpenGL is the core library that contains the majority of the functionality, there are a
number of associated libraries that are also useful. The following is a summary of the most
important of the OpenGL family of graphics libraries:
 OpenGL: the core library, it is platform (i.e. hardware system) independent, but not
windows-system independent (i.e. the code for running on Microsoft Windows will be
different to the code for running on the UNIX environments X-Windows or Gnome).
 glut: The GL Utilities Toolkit, it contains some extra routines for drawing 3-D objects and
other primitives. Using glut with OpenGL enables us to write windows-system independent
code.
 glu: The OpenGL Utilities, it contains some extra routines for projections and rendering
complex 3-D objects.
 glui: Contains some extra routines for creating user-interfaces.

Every routine provided by OpenGL or one of the associated libraries listed above follows the same
basic rule of syntax:
 The prefix of the function name is either gl, glu, or glut, depending on which of these three
libraries the routine is from.

KC Instr.H.Kedir Dept of cs Page 18


 The main part of the function name indicates the purpose of the function.
 The suffix of the function name indicates the number and type of arguments expected by
the function. For example, the suffix 3f indicates that 3 floating point arguments are
expected.
For example, the function glVertex2i is an OpenGL routine that takes 2 integer arguments and
defines a vertex.

Some function arguments can be supplied as predefined symbolic constants. (These are basically
identifiers that have been defined using the C++ #define statement.) These symbolic constants are
always in capital letters, and have the same prefix convention as function names. For example,
GL_RGB, GL_POLYGON and GLUT_SINGLE are all symbolic constants used by OpenGL and
its associated libraries.

Finally, OpenGL has a number of built-in data types to help make it into a platform-independent
package. For example, the C++ int data type may be stored as a 16-bit number on some platforms
but as a 32-bit number on others. Therefore if we use these standard C++ data types with OpenGL
routines the resultant code will not be platform-independent. OpenGL provides its own data types
to overcome this limitation. Mostly, they have the same names as C++ data types but with the
prefix GL attached. For example, GLshort, GLint, GLfloat and GLdouble are all built-in OpenGL
data types. Although you will find that your OpenGL code will still work on your computer if you
do not use these data types, it will not be as portable to other platforms so it is recommended that
you do use them.

5.2. Getting Started with OpenGL

The first thing we need to know to be able to start writing OpenGL programs is which header files
to include. Exactly which header files are required depends upon which library(s) are being used.
For example,
If we are only using the core OpenGL library, then the following line must be added near
the beginning of your source code:
#include <GL/gl.h>

If we also want to use the GL Utilities library, we must add the following line:
#include <GL/glu.h>

For the glui user-interface library we must add:


#include <GL/glui.h>

If we want to use the glut library (and this makes using OpenGL a lot easier) we do not need to
include the OpenGL or glu header files. All we need to do is include a single header file for glut:

KC Instr.H.Kedir Dept of cs Page 19


#include <GL/glut.h>
This will automatically include the OpenGL and glu header files too.

As well as including the appropriate header files, we should also link in the appropriate libraries
when compiling. For Dev-C++, all you need to do is select Multimedia/glut as the project type
when creating your new project – the rest will be done for you.

Now we are ready to start writing OpenGL code. Refer to the code listing in Appendix A for the
following discussion. Before displaying any graphics primitives we always need to perform some
basic initialisation tasks, including creating a window for display. The following lines initialise
the glut library, define a frame buffer and create a window for display.

glutInit(&argc, argv);
glutInitWindowSize(640,480);
glutInitWindowPosition(10,10);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutCreateWindow(“GLUT Points demo”);

Let us examine each of these routines in turn:


 glutInit(&argc, argv);
o This line initialises the glut library. We must call this function before any others in
our glut/OpenGL programs.
 glutInitWindowSize(640,480);
o This function call sets the size of the display window in pixels.
 glutInitWindowPosition(10,10);
o This line sets the position of the top-left corner of the display window, measured in
pixels from the top-left corner of the monitor screen.
 glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
o This line defines the type of frame buffer we want to have. In this case, we are
asking for a single RGB (i.e. colour) frame buffer.
 glutCreateWindow(“GLUT Points demo”);
o Finally, this function call creates the display window with the attributes defined by
the previous calls.

5.3. The OpenGL Event Loop


OpenGL is an event-driven package. This means that it always executes code in response to events.
The code that is executed is known as a callback function, and it must be defined by the
programmer. OpenGL will continually detect a number of standard events in an event loop, and

KC Instr.H.Kedir Dept of cs Page 20


execute the associated callback functions. For example, mouse clicks and keyboard presses are
considered as OpenGL events.

The most important event is the display event. This occurs whenever OpenGL decides that it needs
to redraw the display window. It is guaranteed to happen once at the beginning of the event loop;
after this it will occur whenever the window needs to be redrawn, for example if it is hidden by
another window and then uncovered. Any primitives that we want to draw should be drawn in the
display event callback function; otherwise they will not be displayed.

To specify a callback function we must first write a programmer-defined function, and then specify
that this function should be associated with a particular event. For example, the following code
specifies that the programmer-defined function myDisplay will be executed in response to the
display event:

glutDisplayFunc(myDisplay);

Therefore all OpenGL programs consist of a number of parts:


 Perform any initialisation tasks such as creating the display window.
 Define any required callback functions and associate them with the appropriate events.
 Start the event loop.
To start the event loop we write:
glutMainLoop();
Note that this should be the last statement in your program – after the event loop is started normal
program execution will be suspended. Look at the listing in Appendix A and see if you can identify
the three parts mentioned above.

5.4. OpenGL Initialisation Routines


Now we are ready to start drawing graphics primitives. The first thing we need to understand is
that the OpenGL graphics package is a state system. This means that it maintains a list of state
variables, or state parameters, which will be used to define how primitives will be drawn. These
state variables are specified separately, before the primitives are drawn. For example, if we want
to draw a blue point on our display window, we first set the drawing colour state variable to blue,
and then we draw the point at the specified location.

Therefore, before we actually draw any primitives, we need to set the values of some state
variables. The listing in Appendix A is for an OpenGL program that draws some points on the
display window, so before we draw the points we assign values for three state variables (at the
beginning of the myInit function):

KC Instr.H.Kedir Dept of cs Page 21


glClearColor(1.0, 1.0, 1.0, 0.0);
glColor3f(0,0,0);
glPointSize(4.0);

In the first line we are defining the background colour to have an RGB value of (1,1,1), which
means white. When we clear the screen later on it will be cleared to white. The fourth argument to
glClearColor is the alpha value of the background colour. The alpha value indicates the opacity
of the colour (i.e. is it a semi-transparent colour?): a value of 1 means that it is completely opaque,
or the transparency is zero. The second line listed above defines the current drawing colour to be
(0,0,0), or black. Any subsequently drawn primitive will be drawn in this colour. The final line
sets the point size to four pixels. Any subsequent point primitives drawn will be drawn with this
size.

5.5. OpenGL Coordinate Systems


The last three lines of the myInit function are:

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, 640.0, 0.0, 480.0);

These lines define how the 3-D points will be projected onto the 2-D image plane. We will return
to this important subject in Chapter 5, but for the moment we will give a simplified version of what
really happens. We start off with our graphics primitives defined in world coordinates, then using
a 3-D to 2-D projection transformation we draw a picture of how the primitives would appear if
we took their picture using a virtual camera. The final primitives, drawn in the display window,
are in screen coordinates. Therefore we must specify to OpenGL how it should map points in
world coordinates to points in screen coordinates. The three lines given above define this mapping.
In effect, we are saying that any primitive whose x coordinate lies between 0 and 640, and whose
y coordinate lies between 0 and 480, will be seen by the virtual camera and therefore drawn in the
display window.

See Appendix A for a full listing of the simple OpenGL program we have worked through in this
section. In particular note the following routines in the display function:
 glClear: This function is used to clear the screen before drawing. The background colour
set earlier will be used for clearing.
 glBegin, glVertex2i, glEnd: This sequence of commands draws a number of point
primitives. Actually the glBegin … glEnd pair of commands is used to draw many different
types of primitive in OpenGL, with the symbolic constant argument to glBegin defining
how the vertices in between should be interpreted. In this case, GL_POINTS means that
each vertex should be considered to be a point primitive.

KC Instr.H.Kedir Dept of cs Page 22



glFlush: When we draw primitives in OpenGL it sometimes queues the routines in a buffer.
This function tells OpenGL to ‘flush’ this buffer, i.e. to draw any primitives now to the
frame buffer.
Appendix A – A Simple OpenGL Program

#include <GL/glut.h>
#include <stdlib.h>
void myInit(void) {
glClearColor(1.0, 1.0, 1.0, 0.0); // white background
glColor3f(0,0,0); // black foreground
glPointSize(4.0); // size of points to be drawn

// establish a coordinate system for the image


glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, 640.0, 0.0, 480.0);
}
/* GLUT display callback handler */
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT); // Clear Screen
glBegin(GL_POINTS); // draw 3 points
glVertex2i(100,50);
glVertex2i(100,130);
glVertex2i(150,130);
glEnd();
glFlush(); // send all output to the display
}
/* Program entry point */
int main(int argc, char *argv[])
{
glutInit(&argc, argv); // initialise the glut library
glutInitWindowSize(640,480); // set size of the window
glutInitWindowPosition(10,10); // position of window
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutCreateWindow("GLUT Points demo");
glutDisplayFunc(display); // set display callback
myInit(); // perform other initialisation
glutMainLoop(); // enter the GL event loop
return EXIT_SUCCESS;
}

KC Instr.H.Kedir Dept of cs Page 23


Part II:- Graphics Primitives

1. Introduction

All graphics packages construct pictures from basic building blocks known as graphics 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).

In the following sections we will examine some algorithms for drawing different primitives, and
where appropriate we will introduce the routines for displaying these primitives in OpenGL.

2. OpenGL Point Drawing Primitives

The most basic type of primitive is the point. Many graphics packages, including OpenGL, provide
routines for displaying points. We have already seen the OpenGL routines for point drawing in the
simple OpenGL program introduced in Chapter 1. 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. 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();

Note that when we specify 2-D points, OpenGL will actually create 3-D points with the third
coordinate (the z-coordinate) equal to zero. Therefore, there is not really any such thing as 2-D
graphics in OpenGL – but we can simulate 2-D graphics by using a constant z-coordinate.

3. 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).

KC Instr.H.Kedir Dept of cs Page 24


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)

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:

 y end
 y0 
m …………………………………………………………… (2)
xend x0 
c  y0  mx0 …………………………………………………………………… (3)

Furthermore, for any given x-interval δx, we can calculate the corresponding y-interval δy:

δy = m.δx …………………………………………………………………… (4)


δx = (1/m).δy ………………………….………………………………………… (5)

These equations form the basis of the two line-drawing algorithms described below: the DDA
algorithm and Bresenham’s algorithm.

3.1. DDA Line-Drawing Algorithm

The Digital Differential Analyser (DDA) algorithm operates by starting at one end-point of the
line, and then using Eqs. (4) and (5) to generate successive pixels until the second end-point is
reached. Therefore, first, we need to assign values for δx and δy.

Before we consider the actual DDA algorithm, let us consider a simple first approach to this
problem. Suppose we simply increment the value of x at each iteration (i.e. δx = 1), and then
compute the corresponding value for y using Eqs. (2) and (4). This would compute correct line
points but, as illustrated by Figure 16, 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.

Figure 16 – ‘Holes’ in a Line Drawn by Incrementing x and Computing the Corresponding


y-Coordinate

KC Instr.H.Kedir Dept of cs Page 25


The solution to this problem is to make sure that both δx and δy have values less than or equal to
one. To ensure this, we must first check the size of the line gradient. The conditions are:
 If |m| ≤ 1:
o δx = 1
o δy = m
 If |m| > 1:
o δx = 1/m
o δy = 1
Once we have computed values for δx and δy, the basic DDA algorithm is:
 Start with (x0,y0)
 Find successive pixel positions by adding on (δx, δy) and rounding to the nearest integer,
i.e.
o xk+1 = xk + δx
o yk+1 = yk + δy
 For each position (xk,yk) computed, plot a line point at (round(xk),round(yk)), where the
round function will round to the nearest integer.

Note that the actual pixel value used will be calculated by rounding to the nearest integer, but we
keep the real-valued location for calculating the next pixel position.

Let us consider an example of applying the DDA algorithm for drawing a straight-line segment.
Referring to see Figure 17, we first compute a value for the gradient m:

 yend
 y 0  (13  10) 3
m    0.6
xend
 x0  (15  10) 5
Now, because |m| ≤ 1, we compute δx and δy as follows:

δx = 1
δy = 0.6

Using these values of δx and δy we can now start to plot line points:
 Start with (x0,y0) = (10,10) – colour this pixel
 Next, (x1,y1) = (10+1,10+0.6) = (11,10.6) – so we colour pixel (11,11)
 Next, (x2,y2) = (11+1,10.6+0.6) = (12,11.2) – so we colour pixel (12,11)
 Next, (x3,y3) = (12+1,11.2+0.6) = (13,11.8) – so we colour pixel (13,12)
 Next, (x4,y4) = (13+1,11.8+0.6) = (14,12.4) – so we colour pixel (14,12)
 Next, (x5,y5) = (14+1,12.4+0.6) = (15,13) – so we colour pixel (15,13)
 We have now reached the end-point (xend,yend), so the algorithm terminates

KC Instr.H.Kedir Dept of cs Page 26


Figure 17 - The Operation of the DDA Line-Drawing Algorithm
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.

3.2. 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 18: 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.

Figure 18 - Bresenham's Line-Drawing Algorithm

KC Instr.H.Kedir Dept of cs Page 27


Bresenham’s algorithm works as follows. First, we denote by dupper and dlower the distances between
the centres of pixels A and B and the ‘true’ line (see Figure 18). Using Eq. (1) the ‘true’ y-
coordinate at xk+1 can be calculated as:

y  m( xk  1)  c …………………………………………………………… (6)

Therefore we compute dlower and dupper as:

dlower  y  yk  m( xk  1)  c  yk …………………………………………… (7)


dupper  ( yk  1)  y  yk  1  m( xk  1)  c …………………………………… (8)

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:

d lower  d upper  2mxk  1  2 yk  2c  1 …………………………………… (9)

If the value of this expression is positive we choose pixel A; otherwise we choose pixel B. The
question now is how we can compute this value efficiently. To do this, we define a decision
variable pk for the kth step in the algorithm and try to formulate pk so that it can be computed using
only integer operations. To achieve this, we substitute m  y / x (where Δx and Δy are the
horizontal and vertical separations of the two line end-points) and define pk as:
pk  x(d lower  d upper )  2yxk  2xyk  d …………………………… (10)

Where d is a constant that has the value 2y  2cx  x . 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.

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:
 Always xk+1 = xk+1
 If pk < 0, then yk+1 = yk, otherwise yk+1 = yk+1

Therefore we can define the incremental calculation as:

p k 1  p k  2y , if pk < 0 …………………………………………………… (11)


p k 1  p k  2y  2x , if pk ≥ 0 …………………………………………… (12)

KC Instr.H.Kedir Dept of cs Page 28


The initial value for the decision variable, p0, is calculated by substituting xk = x0 and yk = y0 into
Eq. (10), which gives the following simple expression:

p0  2y  x …………………………………………………………… (13)

So we can see that we never need to compute the value of the constant d in Eq. (10).

Summary

To summarise, we can express Bresenham’s algorithm as follows:


 Plot the start-point of the line (x0,y0)
 Compute the first decision variable:
o p0  2y  x
 For each k, starting with k=0:
o If pk < 0:
 Plot (xk+1,yk)
 p k 1  p k  2y
o Otherwise:
 Plot (xk+1,yk+1)
 p k 1  p k  2y  2x

The steps given above will work for lines with positive |m| < 1. For |m| > 1 we simply swap the
roles of x and y. For negative slopes one coordinate decreases at each iteration whilst the other
increases.

Exercise

Consider the example of plotting the line shown in Figure 17 using Bresenham’s algorithm:
 First, compute the following values:
o Δx = 5
o Δy = 3
o 2Δy = 6
o 2Δy - 2Δx = -4
o p0  2y  x  2  3  5  1
 Plot (x0,y0) = (10,10)
 Iteration 0:
o p0 ≥ 0, so
 Plot (x1,y1) = (x0+1,y0+1) = (11,11)
 p1  p0  2y  2x  1  4  3
 Iteration 1:
o p1 < 0, so
 Plot (x2,y2) = (x1+1,y1) = (12,11)
 p2  p1  2y  3  6  3

KC Instr.H.Kedir Dept of cs Page 29


 Iteration 2:
o p2 ≥ 0, so
 Plot (x3,y3) = (x2+1,y2+1) = (13,12)
 p3  p 2  2y  2x  3  4  1
 Iteration 3:
o p3 < 0, so
 Plot (x4,y4) = (x3+1,y3) = (14,12)
 p 4  p3  2y  1  6  5
 Iteration 4:
o p4 ≥ 0, so
 Plot (x5,y5) = (x4+1,y4+1) = (15,13)
 We have reached the end-point, so the algorithm terminates

We can see that the algorithm plots exactly the same points as the DDA algorithm but it computes
those using only integer operations. For this reason, Bresenham’s algorithm is the most popular
choice for line-drawing in computer graphics.

3.3. 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()

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.

Glint p1[] = {200,100}; Glint p2[] = {50,0}


Glint p3[] = {100,200}; Glint p4[] = {150,0};
Glint p5[] = {0,100};

KC Instr.H.Kedir Dept of cs Page 30


glBegin(GL_LINES);
glVertex2iv(p1);
glVertex2iv(p2);
glVertex2iv(p3);
glVertex2iv(p4);
glVertex2iv(p5);
glEnd();

glBegin(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();
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.

4. 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:

( x  xc )2  ( y  yc )2  r 2 …………………………………………………… (14)

where (xc,yc) is the centre of the circle. Alternatively, in polar coordinates we can write:

x  xc  r cos …………………………………………………………… (15)


y  yc  r sin …………………………………………………………… (16)

In the following sections we will examine a number of approaches to plotting points on a circle,
culminating in the most efficient algorithm: the midpoint algorithm.

KC Instr.H.Kedir Dept of cs Page 31


4.1. Plotting Points Using Cartesian Coordinates

As a first attempt at a circle-drawing algorithm we can use the Cartesian coordinate representation.
Suppose we successively increment the x-coordinate and calculate the corresponding y-coordinate
using Eq. (14):

y  yc  r 2  xc  x 
2
…………………………………………………… (17)

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 16) we would end up with ‘holes’
in the line – see Figure 19. 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. However, with the DDA algorithm we only needed
to compute and check the gradient once for the entire line, but for circles the gradient changes with
each point plotted, so we would need to compute and check the gradient at each iteration of the
algorithm. This fact, in addition to the square root calculation in Eq. (17), would make the
algorithm quite inefficient.

Figure 19 - Circle-Drawing by Plotting Cartesian Coordinates

4.2. 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

KC Instr.H.Kedir Dept of cs Page 32


 y = 5 + 10 sin 5o = 5.87
 Therefore we plot (15,6)
Increase θ to 10o:
 x = 5 + 10 cos 10o = 14.85
 y = 5 + 10 sin 10o = 6.73
 Therefore we plot (15,7)
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 θ. In fact, if we use θ = 1/r (where r is measured in pixels, and θ in radians) we will
get points exactly 1 pixel apart and so there is guaranteed to be no holes.

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 20).

Figure 20 - Circle-Drawing by Plotting Polar Coordinates

4.3. 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 21, 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 means that we only need to compute boundary points for one octant (i.e. one
eighth) of the circle boundary – shown in red in Figure 21.

In addition to generating extra points, the 4-way symmetry of circles has another advantage if
combined with the Cartesian plotting algorithm described in Section 4.1. Recall that this algorithm
resulted in holes in some parts of the circle boundary (see Figure 19), which meant that a time-
consuming gradient computation had to be performed for each point. In fact, the problem of holes

KC Instr.H.Kedir Dept of cs Page 33


in the boundary only occurs when the gradient is greater than 1 for computing x-coordinates, or
when the gradient is less than or equal to 1 for computing y-coordinates. Now that we know we
only need to compute points for one octant of the circle we do not need to perform this check. For
example, in the red octant in Figure 21, we know that the gradient will never become greater than
one, so we can just increment the y-coordinate and compute the corresponding x-coordinates.

Figure 21 - Four-Way Symmetry of Circles

However, even with these efficiency improvements due to symmetry, we still need to perform a
square root calculation (for Cartesian plotting) or a trigonometric calculation (for polar plotting).
It would be nice if there were an algorithm for circle-drawing that used integer operations only, in
the same way that Bresenham’s algorithm does for line-drawing.

4.4. Midpoint Circle-Drawing Algorithm

The midpoint algorithm takes advantage of the symmetry property of circles to produce a more
efficient algorithm for drawing circles. The algorithm works in a similar way to Bresenham’s line-
drawing algorithm, in that it formulates a decision variable that can be computed using integer
operations only.

The midpoint algorithm is illustrated in Figure 22. Recall that we only need to draw one octant of
the circle, as the other seven octants can be generated by symmetry. In Figure 22 we are drawing
the right-hand upper octant – the one with coordinate (y,x) in Figure 21, but the midpoint algorithm
would work with slight modifications whichever octant we chose. Notice that in this octant, when
we move from one pixel to try to draw the next pixel there is only a choice of two pixels: A and B,
or (xk+1,yk) and (xk+1,yk-1). Therefore we don’t need to calculate a real-valued coordinate and then
round to the nearest pixel, we just need to make a decision as to which of the two pixels to choose.

KC Instr.H.Kedir Dept of cs Page 34


Figure 22 - The Midpoint Circle-Drawing Algorithm

We start by defining a function fcirc as follows:

fcirc ( x, y)  x2  y 2  r 2 ………………………………………………….. (18)

This term can be derived directly from Eq. (14). Based on the result of this function, we can
determine the position of any point relative to the circle boundary:
 For points on circle, fcirc= 0
 For points inside circle, fcirc< 0
 For points outside circle, fcirc> 0

Now referring again to Figure 22, we note that the position of the midpoint of the two pixels A and
B can be written as:

midk = (xk+1,yk-0.5) ………………………………………………………….. (19)

Now we can see from Figure 22 that if the midpoint lies inside the circle boundary the next pixel
to be plotted should be pixel A. Otherwise it should be pixel B. Therefore we can use the value of
fcirc(midk) to make the decision between the two candidate pixels:
 If fcirc(midk) < 0, choose A
 Otherwise choose B

In order to make this decision quickly and efficiently, we define a decision variable pk, by
combining Eqs. (18) and (19):

pk  f circ xk  1, yk  0.5  xk  1   yk  0.5  r 2


2 2
………………….. (20)

An incremental calculation for pk+1 can be derived by subtracting pk from pk+1 and simplifying –
the result is (Therefore we can define the incremental calculation as):

pk 1  pk  2 xk 1  1 , if pk < 0 …………………………………………… (21)


pk 1  pk  2 xk 1  1  2 yk 1 , if pk ≥ 0 …………………………………… (22)
The initial value of the decision variable, p0, is calculated by evaluating it at the starting point (0,r):

KC Instr.H.Kedir Dept of cs Page 35


p0  f circ 1, r  0.5  1  r  0.5  r 2 
5
r.
2
…………………………… (23)
4

If r is an integer, then all increments are integers and we can round Eq. (23) to the nearest integer:

p0 = 1 – r …………………………………………………………………… (24)

Summary

To summarise, we can express the midpoint circle-drawing algorithm for a circle centred at the
origin as follows:
 Plot the start-point of the circle (x0,y0) = (0,r)
 Compute the first decision variable:
o p0  1  r
 For each k, starting with k=0:
o If pk < 0:
 Plot (xk+1,yk)
 pk 1  pk  2 xk 1  1
o Otherwise:
 Plot (xk+1,yk-1)
 pk 1  pk  2 xk 1  1  2 yk 1

Example

For example, given a circle of radius r=10, centred at the origin, the steps are:
 First, compute the initial decision variable:
o p0  1  r  9
 Plot (x0,y0) = (0,r) = (0,10)
 Iteration 0:
o p0 < 0, so
 Plot (x1,y1) = (x0+1,y0) = (1,10)
 p1  p0  2 x1  1  9  3  6
 Iteration 1:
o p1 < 0, so
 Plot (x2,y2) = (x1+1,y1) = (2,10)
 p2  p1  2 x2  1  6  5  1
 Iteration 2:
o p2 < 0, so
 Plot (x3,y3) = (x2+1,y2) = (3,10)
 p3  p 2  2 x3  1  1  7  6
 Iteration 3:
o p3 ≥ 0, so
 Plot (x4,y4) = (x3+1,y3-1) = (4,9)
KC Instr.H.Kedir Dept of cs Page 36
 p 4  p3  2 x 4  1  2 y 4  6  9  3
 Iteration 4:
o p4 < 0, so
 Plot (x5,y5) = (x4+1,y4) = (5,9)
 p5  p 4  2 x5  1  3  11  8
 Iteration 5:
o p5 ≥ 0, so
 Plot (x6,y6) = (x5+1,y5-1) = (6,8)
 p 6  p5  2 x6  1  2 y 6  8  3  5
 Etc.

5. Ellipse-Drawing Algorithms

Some graphics packages may provide routines for drawing ellipses, although as we will see ellipses
cannot be drawn as efficiently as circles. The equation for a 2-D ellipse in Cartesian coordinates
is:

2
 x  xc   y  yc 
2

     1 …………………………………………………… (25)
 r 
 x   y 
r

Alternatively, in polar coordinates, we can write:

x = xc + rx cos θ …………………………………………………………… (26)


y = yc + ry sin θ …………………………………………………………… (27)

To understand these equations, refer to Figure 23. Notice that Eqs. (25)-(27) are similar to the
circle equations given in Eqs. (14)-(16), except that ellipses have two radius parameters: rx and ry.
The long axis (in this case, rx) is known as the major axis of the ellipse; the short axis (in this case
ry) is known as the minor axis. The ratio of the major to the minor axis lengths is called the
eccentricity of the ellipse.

Figure 23 - Ellipse Drawing

KC Instr.H.Kedir Dept of cs Page 37


5.1. The Midpoint Algorithm for Ellipse Drawing

The most efficient algorithm for drawing ellipses is an adaptation of the midpoint algorithm for
circle-drawing. Recall that in the midpoint algorithm for circles we first had to define a term whose
sign indicates whether a point is inside or outside the circle boundary, and then we applied it to
the midpoint of the two candidate pixels. For ellipses, we define the function fellipse as:

f ellipse ( x, y)  ry2 x 2  rx2 y 2  rx2 ry2 ………………………………………….. (28)

Eq. (28) can be derived from Eq. (25) by assuming that (xc,yc) = (0,0), and then multiplying both
sides by rx2 ry2 . Now we can say the same for fellipse as we did for the circle function:
 For points on ellipse fellipse= 0
 For points inside ellipse fellipse< 0
 For points outside ellipse fellipse> 0

But at this point we have a slight problem. For circles there was 4-way symmetry so we only
needed to draw a single octant. This meant we always had a choice of just 2 pixels to plot at each
iteration. But ellipses only have 2-way symmetry, which means we have to draw an entire quadrant
(see Figure 24).

Therefore the midpoint algorithm for ellipses is:


 At each point, compute gradient m:
o If |m| ≤ 1 we increment x and decide between candidate y positions
o If |m| >1 we increment y and decide between candidate x positions

Again, a fast incremental algorithm exists to compute a decision function, but because of the
gradient calculation the midpoint ellipse-drawing algorithm is not as efficient as the midpoint
circle drawing algorithm.

Figure 24 - Two-Way Symmetry of Ellipses

KC Instr.H.Kedir Dept of cs Page 38


6. 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 colour 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 25).

Figure 25 - A Polygon with an Edge Crossing

Polygons are the most common form of graphics primitive because they form the basis of
polygonal meshes, which is the most common representation for 3-D graphics objects. Polygonal
meshes approximate curved surfaces by forming a mesh of simple polygons. Some examples of
polygonal meshes are shown in Figure 26.

Figure 26 - Examples of Polygonal Mesh Surfaces

6.1. Convex and Concave Polygons

We can differentiate between convex and concave polygons:


 Convex polygons have all interior angles ≤ 180o
 Concave polygons have at least one interior angle > 180o
The difference between convex and concave polygons is shown in Figure 272. Many graphics
packages (including OpenGL) require all fill-area polygons to be convex polygons. The reason for
this, as we will see in Chapter 3, is that it is much more efficient to fill a polygon if we know it is

KC Instr.H.Kedir Dept of cs Page 39


convex. Some packages even require that all fill-area primitives are triangles, as this makes filling
even more straightforward.

(a) (b)

Figure 27 – Types of Polygon: (a) Convex; (b) Concave

Most graphics packages also insist on some other conditions regarding polygons. Polygons may
not be displayed properly if any of the following conditions are met:
 The polygon has less than 3 vertices; or
 The polygon has collinear vertices; or
 The polygon has non-coplanar vertices; or
 The polygon has repeated vertices.
Polygons that meet one of these conditions are often referred to as degenerate polygons.
Degenerate polygons may not be displayed properly by graphics packages, but many packages
(including OpenGL) will not check for degenerate polygons as this takes extra processing time
which would slow down the rendering process. Therefore it is up to the programmer to make sure
that no degenerate polygons are specified.

How can we identify if a given polygon is convex? A number of approaches exist:


 Compute and check all interior angles – if one is greater than 180o then the polygon is
concave; otherwise it is convex.
 Infinitely extend each edge, and check for an intersection of the infinitely extended edge
with other edges in the polygon. If any intersections exist for any edge, the polygon is
concave; otherwise it is convex.
 Compute the cross-product of each pair of adjacent edges. If all cross-products have the
same sign for their z-coordinate (i.e. they point in the same direction away from the plane
of the polygon), then the polygon is convex; otherwise it is concave.

The last technique used the cross-product of vectors. The result of the cross-product of two vectors
is a vector perpendicular to both vectors, whose magnitude is the product of the two vector
magnitudes multiplied by the sin of the angle between them:
 
N  u E1 E2 sin , where 0 ≤ θ ≤ 180o …………………………………… (29)

KC Instr.H.Kedir Dept of cs Page 40


The direction of the cross-product is determined by the right-hand rule:
 For V1xV2, with your right-hand grasp an axis that is perpendicular to V1 and V2, so that
the fingers curl from V1 to V2 forming an angle <180o. The direction of the cross-product
is the direction of your right thumb.
This is illustrated in Figure 28. If you imagine V1 pointing along the x-axis and V2 pointing along
the y-axis, then the z-axis is the direction of the cross-product. This is true so long as the angle
between V1 and V2 is less than 180o. If it becomes greater than 180o, then we would need to switch
V1 and V2 (i.e. V1 is the y-axis and V2 the x-axis), so that the angle between them became less
than 180o again. In this case the cross-product would point in the opposite direction.

Figure 28 - The Right-Hand Rule for Computing Cross-Products

6.1.1. Splitting Concave Polygons

Because most graphics packages insist on polygons being convex, once we have identified concave
polygons we need to split them up to create a number of convex polygons.

One technique for splitting concave polygons is known as the vector technique. This is illustrated
in Figure 29, and can be summarised as follows:
 Compute the cross-product of each pair of adjacent edge vectors.
 When the cross-product of one pair has a different sign for its z-coordinate compared to
the others (i.e. it points in the opposite direction):
o Extend the first edge of the pair until it intersects with another edge.
o Form a new vertex at the intersection point and split the polygon into two.
Recall from Section 6.1 that the cross-product switches direction when the angle between the
vectors becomes greater than 180o. Therefore the vector technique is detecting this condition by
using a cross-product.

KC Instr.H.Kedir Dept of cs Page 41


(a) (b) (c)

Figure 29 - Splitting a Concave Polygon into Convex Polygons

6.2. Polygon Inside-Outside Tests

In order to fill polygons we need some way of telling if a given point is inside or outside the
polygon boundary: we call this an inside-outside test. We will see in Chapter 7 that such tests are
also useful for the ray-tracing rendering algorithm.

We will examine two different inside-outside tests: the odd-even rule and the nonzero winding
number rule. Both techniques give good results, and in fact usually their results are the same, apart
from for some more complex polygons.

6.2.1. Odd-Even Rule

The odd-even rule is illustrated in Figure 30(a). Using this technique, we determine if a point P is
inside or outside the polygon boundary by the following steps:
 Draw a line from P to some distant point (that is known to be outside the polygon
boundary).
 Count the number of crossings of this line with the polygon boundary:
o If the number of crossings is odd, then P is inside the polygon boundary.
o If the number of crossings is even, then P is outside the polygon boundary.
We can see from Figure 30(a) that the two white regions are considered to be outside the polygon
since they have two line crossings to any distant points.

6.2.2. Nonzero Winding Number Rule

The nonzero winding number rule is similar to the odd-even rule, and is illustrated in Figure 30(b).
This time we consider each edge of the polygon to be a vector, i.e. they have a direction as well as
a position. These vectors are directed in a particular order around the boundary of the polygon (the
programmer defines which direction the vectors go). Now we decide if a point P is inside or outside
the boundary as follows:
 Draw a line from P to some distant point (that is known to be outside the polygon
boundary).

KC Instr.H.Kedir Dept of cs Page 42


 At each edge crossing, add 1 to the winding number if the edge goes from right to left, and
subtract 1 if it goes from left to right.
o If the total winding number is nonzero, P is inside the polygon boundary.
o If the total winding number is zero, P is outside the polygon boundary.
We can see from Figure 30(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 30 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.

(a) (b)

Figure 30 - Inside-Outside Tests for Polygons

6.3. Representing Polygons

Polygons, and in particular convex polygons, are the most common type of primitive in 3-D
graphics because they are used to represent polygonal meshes such as those shown in Figure 26.
But how can polygonal meshes be represented? A common technique is to use tables of data. These
tables can be of two different types:
 Geometric tables: These store information about the geometry of the polygonal mesh, i.e.
what are the shapes/positions of the polygons?
 Attribute tables: These store information about the appearance of the polygonal mesh, i.e.
what colour is it, is it opaque or transparent, etc. This information can be specified for each
polygon individually or for the mesh as a whole.

Figure 31 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

KC Instr.H.Kedir Dept of cs Page 43


coordinates are only stored once, in the vertex table, and also information about each edge is only
stored once.

Figure 31 - A Geometric Table for Representing Polygons

6.4. Polygon Front and Back Faces

Most polygons are part of a 3-D polygonal mesh, which are often enclosed (solid) objects.
Therefore when storing polygon information we need to know which face of the polygon is facing
outward from the object. Every polygon has two faces: the front-face and the back-face. The front-
face of a polygon is defined as the one that points outward from the object, whereas the back-face
points towards the object interior.

Often in graphics we need to decide if a given point is on one side of a polygon or the other. For
example, if we know the position of the virtual camera we may want to decide if the camera is
looking at the front-face or the back-face of the polygon. (It can be more efficient for graphics
packages not to render back-faces.)

We can make this calculation using the standard equation of a plane:

Ax + By + Cz + D = 0 …………………………………………………… (30)

Given at least three coplanar points (e.g. polygon vertices) we can always calculate the values of
the coefficients A, B, C, and D for a plane. Now, for any given point (x,y,z):
 If Ax + By + Cz + D = 0, the point is on the plane.
 If Ax + By + Cz + D < 0, the point is behind the plane.
 If Ax + By + Cz + D > 0, the point is in front of the plane.

KC Instr.H.Kedir Dept of cs Page 44


6.5. Polygon Normal Vectors

Polygon front-faces are usually identified using normal vectors. A normal vector is a vector that
is perpendicular to the plane of the polygon and which points away from front face (see Figure
32).

Figure 32 - The Normal Vector of a Polygon

6.5.1. Computing Normal Vectors

In graphics packages, we can either specify the normal vectors ourselves, or get the package to
compute them automatically. How will a graphics package compute normal vectors automatically?

The simplest approach is to use the cross-product of adjacent edge vectors in the polygon
boundary. Recall that the result of a cross-product is a vector that is perpendicular to the two
vectors. Consider Figure 33. Here we can see three vertices of a polygon: V1, V2 and V3. These
form the two adjacent edge vectors E1 and E2:

E1 = (1,0,0)
E2 = (0,1,0)
 
Now, using Eq. (29) we can compute the surface normal N  u E1 E2 sin , where 0 ≤ θ ≤ 180o,

and the direction of u is determined by the right-hand rule. This will give a vector that is

perpendicular to the plane of the polygon. In this example, N  (0,0,1) .

Notice that if we consider the edge vectors to go the other way round the boundary (i.e. clockwise
instead of anti-clockwise) then the normal vector would point in the other direction. This is the
reason why in most graphics packages it is important which order we specify our polygon vertices
in: specifying them in an anti-clockwise direction will make the normal vector point towards us,
whereas specifying them in a clockwise direction will make it point away from us.

KC Instr.H.Kedir Dept of cs Page 45


Figure 33 - Computing Surface Normals Using a Cross-Product of Adjacent Edges

6.6. 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 colour determined by the current colour 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*

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)

KC Instr.H.Kedir Dept of cs Page 46


For example, Figure 34 shows an example of executing the following call to glRecti:

glRecti(200,100,50,250);

(The black crosses are only shown for the purpose of illustrating where the opposing corners of
the rectangle are.)

In 2-D graphics we don’t need to worry about front and back faces – both faces will be displayed.
But if we use glRect* in 3-D graphics we must be careful. For example, in the above example we
actually specified the vertices in a clockwise order. This would mean that the back-face would be
facing toward the camera. To get an anti-clockwise order (and the front-face pointing towards the
camera), we must specify the bottom-left and top-right corners in the call to glRect*.

Figure 34 - Drawing a 2-D Rectangle with the OpenGL routine glRect*

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 35. Notice that the
vertices of the polygon are specified in anti-clockwise order.

KC Instr.H.Kedir Dept of cs Page 47


glBegin(GL_POLYGON);
glVertex2iv(p1);
glVertex2iv(p2);
glVertex2iv(p3);
glVertex2iv(p4);
glVertex2iv(p5);
glVertex2iv(p6);
glEnd();

Figure 35 - A Polygon Drawn Using the


GL_POLYGON OpenGL Primitive

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 36 illustrates the use of the GL_TRIANGLES primitive.

glBegin(GL_TRIANGLES);
glVertex2iv(p1);
glVertex2iv(p2);
glVertex2iv(p6);
glVertex2iv(p3);
glVertex2iv(p4);
glVertex2iv(p5);
glEnd();

Figure 36 - Triangles Drawn Using the


GL_TRIANGLES OpenGL Primitive

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 37 illustrates the use of GL_TRIANGLE_STRIP.

KC Instr.H.Kedir Dept of cs Page 48


glBegin(GL_TRIANGLE_STRIP);
glVertex2iv(p1);
glVertex2iv(p2);
glVertex2iv(p6);
glVertex2iv(p3);
glVertex2iv(p5);
glVertex2iv(p4);
glEnd();

Figure 37 - Triangles Drawn Using the


GL_TRIANGLE_STRIP OpenGL
Primitive

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 38 for an example
of the use of GL_TRIANGLE_FAN.

glBegin(GL_TRIANGLE_FAN);
glVertex2iv(p1);
glVertex2iv(p2);
glVertex2iv(p3);
glVertex2iv(p4);
glVertex2iv(p5);
glVertex2iv(p6);
glEnd();

Figure 38 - Triangles Drawn Using the


GL_TRIANGLE_FAN OpenGL
Primitive

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. See Figure 39 for an example of GL_QUADS.

KC Instr.H.Kedir Dept of cs Page 49


glBegin(GL_QUADS);
glVertex2iv(p1);
glVertex2iv(p2);
glVertex2iv(p3);
glVertex2iv(p4);
glVertex2iv(p5);
glVertex2iv(p6);
glVertex2iv(p7);
glVertex2iv(p8);
glEnd();
Figure 39 - Quadrilaterals Drawn Using the
GL_QUADS OpenGL Primitive

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 40 illustrates the use of GL_QUAD_STRIP.

glBegin(GL_QUAD_STRIP);
glVertex2iv(p1);
glVertex2iv(p2);
glVertex2iv(p3);
glVertex2iv(p4);
glVertex2iv(p5);
glVertex2iv(p6);
glVertex2iv(p7);
glVertex2iv(p8);
glEnd();
Figure 40 - Quadrilaterals Drawn Using the
GL_QUAD_STRIP OpenGL Primitive

7. 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 41(a)). This is a simple representation that allows fast rendering of the character.

KC Instr.H.Kedir Dept of cs Page 50


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. For this reason, when using bitmap fonts we normally need to store
multiple fonts to represent the characters in different sizes/styles etc.

An alternative representation to bitmaps is the stroke, or outline, representation (see Figure 41(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 to draw than
bitmap fonts.

We can also divide character primitives into serif and sans-serif fonts. Sans-serif fonts have no
accents on the characters, whereas serif fonts do have accents. For example, as shown below, Arial
and Verdana fonts are examples of sans-serif fonts whereas Times-Roman and Garamond are serif
fonts:

Arial is a sans-serif font


Verdana is a sans-serif font
Times Roman is a serif font
Garamond is a serif font

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
Arial is a proportional font

KC Instr.H.Kedir Dept of cs Page 51


(a) (b)

Figure 41 - Bitmap and Stroke Character Primitives

8. OpenGL Character Primitive Routines

OpenGL on its own does not contain any 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).

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

We can specify proportional bitmap fonts using the following symbolic constants:
 GLUT_BITMAP_TIMES_ROMAN_10

KC Instr.H.Kedir Dept of cs Page 52


 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

Summary

The following points summarise the key concepts of this chapter:


 Graphics primitives are the basic building blocks of images.
 Geometric primitives describe the geometry, or shape, of these building blocks.
 The GL_POINTS primitive can be used to draw points in OpenGL.
 The DDA (Digital Differential Analyser) line-drawing algorithm computes successive
points by iteratively adding the x and y offsets δx and δy onto the previously computed
location.
 Bresenham’s line-drawing algorithm is the most efficient line-drawing algorithm, and
determines computes successive points by computing a decision function, which can be
calculated using integer calculations only.
 The GL_LINES primitive can be used to draw separate line segments in OpenGL.
 The GL_LINE_STRIP and GL_LINE_LOOP primitives can be used to draw polylines
(connected line segments) in OpenGL.
 Circles can be drawn by plotting Cartesian or polar coordinates.
 The efficiency of any circle-drawing algorithm can be improved by taking advantage of
the four-way symmetry of circles: for each point we compute, we can generate seven more
through symmetry.
 The midpoint algorithm is the most efficient circle-drawing algorithm, and works in a
similar way to Bresenham’s line-drawing algorithm: successive points are determined by
computing a decision function, which can be calculated using integer calculations only.
 The midpoint algorithm can also be adapted to work with ellipses, but ellipses have only
two-way symmetry, so drawing them is less efficient than drawing circles.
 A fill-area primitive is any boundary that can be filled. Most fill-area primitives are
polygons.
 Polygon primitives are typically used to construct 3-D objects in the form of a polygonal
mesh.
 Polygonal meshes are normally represented using a geometric table, which consists of a
vertex table, an edge table and a surface-facet table.
 A simple polygon, or standard polygon, is one with no edge crossings.
 A degenerate polygon is one that has less than three vertices, collinear vertices, non-
coplanar vertices or repeated vertices. Degenerate polygons may not be displayed correctly
by graphics packages.

KC Instr.H.Kedir Dept of cs Page 53


 A convex polygon is one in which all interior angles are less or equal to 180o. If a polygon
has at least one interior angle greater than 180o it is a concave polygon.
 Concave/convex polygons can be identified by taking the cross-product of adjacent pairs
of edge vectors. If one cross-product has a different sign for its z-coordinate, the polygon
is concave.
 The vector technique can be used to split concave polygons into a number of convex
polygons.
 An inside-outside test determines if a given point is inside or outside a polygon boundary.
 Common inside-outside tests are the odd-even rule and the nonzero winding number rule.
 The front-face of a polygon is normally the face that is pointing out from the surface of the
polygonal mesh.
 The back-face of a polygon points in to the interior of the object.
 The front-face of a polygon is normally identified by a surface normal vector.
 Surface normal vectors can be computed by taking the cross-product of any two adjacent
edge vectors.
 OpenGL requires that all polygons are convex polygons.
 The glRect* routine can be used to draw 2-D rectangles in OpenGL.
 The GL_POLYGON, GL_TRIANGLES and GL_QUADS and primitives can be used to
draw fill-area polygons in OpenGL
 The GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN and GL_QUAD_STRIP primitives can
be used to draw connected sets of fill-area polygons in OpenGL.
 Character primitives can be drawn using either bitmap or stroke fonts:
o Bitmap fonts represent characters using a grid of pixel values. They are efficient to
draw but not easily scalable.
o Stroke (or outline) fonts represent characters using line or curve primitives. They
are less efficient to draw but are easily scalable.
 Fonts can be either serif or sans-serif: serif fonts feature accents at the ends of the lines
whereas sans-serif fonts do not.
 Fonts can be either monospace or proportional: monospace fonts use the same width for
each character whereas proportional fonts use a width proportional to the width of the
character.
 The glutBitmapCharacter routine can be used to draw a bitmap character primitive in
OpenGL.
 The glutStrokeCharacter routine can be used to draw a stroke character primitive in
OpenGL.

KC Instr.H.Kedir Dept of cs Page 54


Exercises

1) The figure below shows the start and end points of a straight line.

a. Show how the DDA algorithm would draw the line between the two points.

b. Show how Bresenham’s algorithm would draw the line between the two points.

2) Show how the following circle-drawing algorithms would draw a circle of radius 5 centred
on the origin. You need only consider the upper-right octant of the circle, i.e. the arc shown
in red in the figure below.

a. Plotting points using Cartesian coordinates.


b. Plotting points using polar coordinates.
c. The midpoint algorithm.

3) Show which parts of the fill-area primitive shown below would be classified as inside or
outside using the following inside-outside tests:

a. Odd-even rule.
b. Nonzero winding number rule.

KC Instr.H.Kedir Dept of cs Page 55


4) Write a C++/OpenGL program to read in a string and a sequence of points from a file, and
display the string as a title and the points as triangles using the GL_TRIANGLES primitive.
For example, the file listed below would lead to the following image being displayed.

Triangle_Demo
150 100
150 150
100 100
200 100
300 100
260 200
150 300
200 400
100 300

Exercise Solutions

1) The final line would be as shown below:

a. First we calculate the gradient:


 y  y0   (2  0)  2  0.67
m  end
xend  x0  (3  0) 3
Now, because |m| ≤ 1, we compute δx and δy as follows:
δx = 1
δy = 0.67

Using these values of δx and δy we can now start to plot line points:
o Start with (x0,y0) = (0,0) – colour this pixel
o Next, (x1,y1) = (0+1,0+0.67) = (1,0.67) – so we colour pixel (1,1)
o Next, (x2,y2) = (1+1,1 +0.67) = (2,1.33) – so we colour pixel (2,1)
o Next, (x3,y3) = (2+1,1 +0.67) = (3,2) – so we colour pixel (3,2)
o We have now reached the end-point (xend,yend), so the algorithm terminates

b. First, compute the following values:


o Δx = 3
o Δy = 2

KC Instr.H.Kedir Dept of cs Page 56


o 2Δy = 4
o 2Δy - 2Δx = -2
o p0  2y  x  2  2  3  1
Plot (x0,y0) = (0,0)
Iteration 0:
o p0 ≥ 0, so
 Plot (x1,y1) = (x0+1,y0+1) = (1,1)
 p1  p0  2y  2x  1  2  1
Iteration 1:
o p1 < 0, so
 Plot (x2,y2) = (x1+1,y1) = (2,1)
 p2  p1  2y  1  4  3
Iteration 2:
o p2 ≥ 0, so
 Plot (x3,y3) = (x2+1,y2+1) = (3,2)
 p3  p 2  2y  2x  3  2  1

2) The answers are:

a. We start from x = 0, and then successively increment xand calculate the corresponding y
using Eq. (17): y  yc  r 2  xc  x  . We can tell that we have left the first octant
2

when x > y.
o x = 0, y  0  5 2  0  0  5 , so we plot (0,5).
2

o x = 1, y  0  5 2  0  1  4.9 , so we plot (4.9,1) = (1,5).


2

o x = 2, y  0  5 2  0  2  4.58 , so we plot (4.58,2) = (2,5).


2

o x = 3, y  0  5 2  0  3  4 , so we plot (3,4).
2

o x = 4, y  0  5 2  0  4  3 , so x > y and we stop.


2

The figure below shows the first quadrant of the circle, with the plotted points shown in
blue, and points generated by symmetry in grey.

KC Instr.H.Kedir Dept of cs Page 57


b. First we calculate the angular increment using θ = 1/r radians. This is equal to
(1/5)*(180/π) = 11.46o. Then we start with θ = 90o, and compute x and y according to
Eqs. (15) and (16) for successive value of θ, subtracting 11.46o at each iteration. We stop
when θ becomes less than 45o.
o θ = 90o, x  0  5 cos 90o  0 , y  0  5 sin 90o  5 , so we plot (0,5)
o θ = 78.54o, x  0  5 cos 78.54o  0.99 , y  0  5 sin 78.54o  4.9 , so we plot (1,5)
o θ = 67.08o, x  0  5 cos 67.08o  1.95 , y  0  5 sin 67.08o  4.6 , so we plot (2,5)
o θ = 55.62o, x  0  5 cos 55.62o  2.82 , y  0  5 sin 55.62o  4.13 , so we plot
(3,4)
o θ = 44.16o, which is less than 45o, so we stop.
The points plotted are the same as for the Cartesian plotting algorithm.

c. The steps of the algorithm are:


o Plot the first circle point (0,r) = (0,5)
o Compute the first value of the decision variable p0 = 1 – r = -4
o Iteration 0:
 p0 < 0, so we plot (x0+1,y0) = (1,5)
 p1  p0  2 x1  1  4  2  1  1  1
o Iteration 1:
 p1 < 0, so we plot (x1+1,y1) = (2,5)
 p2  p1  2 x1  1  1  2  2  1  4
o Iteration 2:
 p2 ≥ 0, so we plot (x2+1,y2-1) = (3,4)
 p 3  p 2  2 x3  1  2 y 3  4  2  3  1  2  4  3
o Iteration 3:
 p3 ≥ 0, so we plot (x3+1,y3-1) = (4,3)
 At this point, x > y so we have left the octant and the algorithm stops.

KC Instr.H.Kedir Dept of cs Page 58


Part III:- Attributes of Graphics primitives
1. Introduction

In this handout we will consider the subject of attributes of graphics primitives. We can define an
attribute of a primitive as a parameter that affects the way the primitive is displayed. For example,
the drawing colour and line style are both attributes of the line primitive. We will examine what
attributes there are for different primitives, and look at some algorithms for implementing them.

Recall from Chapter 1 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.

First we will look at colour attributes, which affect almost all primitives. The rest of this chapter
is structured according to the type of primitive that the attribute affects. We will look at attributes
of point primitives, line primitives, fill-area primitives and character primitives. For each we will
describe the underlying algorithms for implementing the change, and introduce how the attribute
can be modified in OpenGL. Finally we will consider the related topic of antialiasing.

2. Colour Attribute

In our first OpenGL program in Chapter 1 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 1, 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 (see Section Error! Reference source not found.). This process is known as colour
blending, and the RGBA colour representation is necessary to achieve colour blending.

2.1. Direct Storage vs. Colour Look-Up Tables

Recall that the frame buffer is used by raster graphics systems to store the image ready for display
on the monitor. Therefore, for colour displays, the frame buffer must have some way of storing

KC Instr.H.Kedir Dept of cs Page 59


colour values. We have just seen that colours can be stored as either RGB or RGBA values.
Whichever representation is used, there are two ways in which graphics packages store colour
values in a frame buffer:
 Direct storage
 Look-up table

Using direct storage, the colour values of each pixel are stored directly in the frame buffer (see
Figure 42). Therefore, each pixel in the frame buffer must have 3 (RGB) or 4 (RGBA) values stored.
This means that the frame buffer can take up a lot of memory. For example, suppose we have a
screen resolution of 1024x768, and we use 8 bits for each of the red, green and blue components
of the colours stored. The total storage required will be 1024x768x24 bits = 2.4MB.

Colour look-up tables are also illustrated in Figure 42. They try to reduce the amount of storage
required by just storing an index for each pixel in the frame buffer. The actual colours are stored
in a separate look-up table, and the index looks up a colour in this table. Therefore, using colour
look-up tables we can only have a limited number of different colours in the scene (the number of
rows in the look-up table), but these colours can be chosen from a palette containing the complete
range of colours. For example, suppose we are using 8 bits for each colour (=24 bits in all), and
the look-up table has 256 rows. In this case the total range of colours we can choose from is 224,
but we can only use 256 of these in any one image.

Although colour look-up tables do save storage, they slow down the rasterisation process as an
extra look-up operation is required. They were commonly used in the early days of computer
graphics when memory was expensive, but these days memory is cheaper so most systems use
direct storage. OpenGL uses direct storage by default, but the programmer can choose to use a
colour look-up table if they wish.

Figure 42 - Direct Storage vs. Colour Look-Up Tables

KC Instr.H.Kedir Dept of cs Page 60


2.2. OpenGL Colour Attributes

We have already seen how to modify the drawing and background colours in the simple OpenGL
program in Chapter 1. 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 1, 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.

3. 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 1 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. Section 2.2 above showed how to change the
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.

4. 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.

KC Instr.H.Kedir Dept of cs Page 61


4.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 43. We can see that for some lines (e.g. Figure 43(a)) we should plot extra
pixels vertically, and for others (e.g. Figure 43(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 43 - 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 44.
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 45.
Depending on whether we are plotting extra pixels in the horizontal or vertical directions,
the line ends will be horizontal or vertical.

KC Instr.H.Kedir Dept of cs Page 62


(a) (b)

Figure 44 - Line Thickness Depends on Its Gradient

Figure 45 – 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 46. 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.

KC Instr.H.Kedir Dept of cs Page 63


Figure 46 - 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 47 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 47 - 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 48. 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 48 - Line Join Types

KC Instr.H.Kedir Dept of cs Page 64


4.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.

4.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);

KC Instr.H.Kedir Dept of cs Page 65


5. Fill-Area Attributes

The most important attribute for fill-area polygons is whether they are filled or not. We can either
draw a filled polygon or drawn the outline only. Drawing the outline only is known as wireframe
rendering. If the polygon is to be filled we can specify a fill style.

In this section we first consider some algorithms for filling polygons and other fill-area primitives.
Then we describe how to modify fill-area attributes in OpenGL.

5.1. Fill Algorithms

Broadly speaking, we can identify two different approaches to filling enclosed boundaries:
 Scan-line approach: We automatically determine which pixels are inside or outside the
boundary for each scan line. These approaches are normally used by general-purpose
graphics packages.
 Seed-based approach: Start from an interactively defined seed point and paint outwards
until we reach a boundary. These approaches are normally used by special-purpose
graphics packages.

5.1.1. Scan-Line Fill Algorithm


leader
Scan-line fill algorithms are automatic (i.e. they require no user intervention) and are commonly
used by general-purpose graphics packages such as OpenGL. One scan-line fill algorithm is
described below.

The scan-line fill algorithm automatically fills any non-degenerate polygon by considering each
scan-line (i.e. row of the frame buffer) in turn. We move across the scan-line, from left-to-right,
until we reach a boundary. Then we start plotting pixels in the fill colour and continue to move
across the scan-line. When we reach another boundary we stop filling. At the next boundary we
start filling again, and so on. We can think of this algorithm as being similar to the odd-even rule
inside-outside test: the starting point (i.e. the far left of the scan-line) is assumed to be outside the
polygon, so points between the first and second boundary crossings are assumed to be inside. This
process is illustrated in Figure 49.

Figure 49 - The Scan-Line Fill Algorithm

KC Instr.H.Kedir Dept of cs Page 66


The scan-line fill algorithm works well for simple polygons, but there are cases in which it can
experience problems. We now consider such a case, illustrated in Figure 50. The scan-line
algorithm must always treat vertices as two boundary crossings: if we did not then as we crossed
the vertex in scan-line x in Figure 50 the algorithm would stop filling when it should continue.
However, if we treat vertices as two boundary crossings it can cause problems in other case, as
shown by scan-line y. Here we treat the vertex as two boundary crossings, but it causes the
algorithm to continue filling when it should stop. The difference between scan-lines x and y is that
in scan-line x the boundary does not cross the scan-line at the vertex, whereas in scan-line y it does.

There are two possible answers to this problem. First, we can perform a preprocessing stage to
detect which vertices cross the scan-line and which don’t. This would work OK, but it also takes
time. Therefore the more common approach is to insist that all polygons are convex. As we can
see from Figure 51, in a convex polygon there is always a maximum of two boundary crossings in
every scan-line. Therefore if there are two crossing we simply fill between the two crossing points.
If there is one we know that it is a vertex crossing so we fill a single pixel. This greatly improves
the efficiency of the scan-line fill algorithm, and is the reason why most graphics packages insist
that all fill-area primitives are convex polygons.

Figure 50 - Problems in the Scan-Line Fill Algorithm: Scan-Line 'x' is Filled Correctly but
Scan-Line 'y' is not

Figure 51 - Scan-Line Fill Algorithm on a Convex Polygon

KC Instr.H.Kedir Dept of cs Page 67


5.1.2. Seed-Based Fill Algorithms

Seed-based fill algorithms have the advantage that they can fill arbitrarily complex shapes. They
are less efficient than scan-line algorithms, and require user interaction in the form of specifying
a seed-point (i.e. a starting point for the fill, known to be inside the boundary). Therefore they are
typically used by interactive packages such as paint programs.

In this section we will examine two similar techniques: boundary-fill and flood-fill. Both are
recursive in nature and work by starting from the seed point and painting outwards until some
boundary condition is met. The difference between the two algorithms is in the nature of the
boundary condition.

5.1.2.1. Boundary Fill

To implement the boundary fill algorithm we first define three parameters: the seed point, a fill
colour and a boundary colour. The pseudocode for the algorithm is:
 Start with seed point
 If the current pixel is not in the boundary colour and not in the fill colour:
o Fill current pixel using fill colour
o Recursively call boundary-fill for all neighbouring pixels

Notice that the termination condition for the algorithm is when we reach pixels in the boundary
colour. Therefore the boundary must be in a single (known) colour. We check to see if the current
pixel is in the fill colour to ensure we don’t fill the same pixel many times.

One question that we must answer before implementing boundary-fill is what exactly we mean by
a neighbouring point. Figure 52 shows that there are two possible interpretations of a neighbour:
4-connected and 8-connected neighbours. Both interpretations can be useful, although using 8-
connected neighbours can lead to the algorithm ‘escaping’ from thin diagonal boundaries, so 4-
connected neighbours are more commonly used.

Figure 52 - 4-Connected and 8-Connected Neighbours

KC Instr.H.Kedir Dept of cs Page 68


Another issue that we must consider is what to do if the region to be filled is already partly filled.
For example, in Figure 53 the boundary colour is grey, the fill colour is red and the seed-point is
the pixel marked by the letter ‘S’. Since boundary-fill will not process any pixels that are already
in the fill colour, in this example only half of the fill-area will be filled: the algorithm will stop
when it reaches the already-filled pixels. One solution to this problem is to pre-process the fill-
area to remove any partly-filled areas.

Figure 53 - A Partly Filled Region

A C++ implementation of boundary fill is given below.

void boundaryFill (int x, int y,


int fillColour,
int borderColour) {
int interiorColour = getpixel (x, y);
if ((interiorColour != borderColour) &&
(interiorColour != fillColour) {
setPixel(x,y,fillColour);
boundaryFill(x,y-1,fillColour,borderColour);
boundaryFill(x-1,y,fillColour,borderColour);
boundaryFill(x+1,y,fillColour,borderColour);
boundaryFill(x,y+1,fillColour,borderColour);
}
}

5.1.2.2. Flood Fill

Flood-fill is very similar to boundary-fill, but instead of filling until it reaches a particular boundary
colour, it continues to fill while the pixels are in specific interior colour. Therefore, first we must
define a seed point, a fill colour and an interior colour. Pseudocode for flood-fill is given below:
 Start with seed point
 If current pixel in interior colour:
o Fill pixel using fill colour
o Recursively call flood-fill for all neighbouring pixels

KC Instr.H.Kedir Dept of cs Page 69


Again, for flood-fill we have the same issues regarding partly-filled areas and what we mean by a
neighbouring pixel. Actually the two algorithms are very similar, and in many cases their operation
will be identical. But flood fill can be more useful for cases where the boundary is not in a single
colour, such as that shown in Figure 54.

Figure 54 - An Enclosed Region Where the Boundary is not in a Single Colour

C++ code to implement the flood-fill algorithm is shown below.

void floodFill (int x, int y,


int fillColour,
int interiorColour) {
int colour = getpixel (x, y);
if (colour == interiorColour) {
setPixel(x,y,fillColour);
floodFill(x+1,y,fillColour,interiorColour);
floodFill(x-1,y,fillColour,interiorColour);
floodFill(x,y+1,fillColour,interiorColour);
floodFill(x,y-1,fillColour,interiorColour);
}
}

5.2. OpenGL Fill-Area Attribute Functions

OpenGL provides a number of features to modify the appearance of fill-area polygons. First, we
can choose to fill the polygon or just display an outline (i.e. wireframe rendering). We do this by
changing the display mode using the glPolygonMode routine. The basic form of this function is:

glPolygonMode(face, displayMode)

Here, the face argument can take any of the following values:
 GL_FRONT: apply changes to front-faces of polygons only.
 GL_BACK: apply changes to back-faces of polygons only.
 GL_FRONT_AND_BACK: apply changes to front and back faces of polygons.

KC Instr.H.Kedir Dept of cs Page 70


The displayMode argument can take any of these values:
 GL_FILL: fill the polygon faces.
 GL_LINE: draw only the edges of the polygons (wireframe rendering)
 GL_POINT: draw only the vertices of the polygons.

For example, the following code draws a polygon with four vertices using wireframe rendering for
the front face.

glPolygonMode (GL_FRONT, GL_LINE);


glBegin(GL_POLYGON);
glVertex2i(350,250);
glVertex2i(400,300);
glVertex2i(400,350);
glVertex2i(300,250);
glEnd();

In addition to changing the polygon drawing mode, we can also specify a fill pattern for polygons.
We do this using the polygon stipple feature of OpenGL. The steps can be summarised as:
 Define a fill pattern
 Enable the polygon stipple feature of OpenGL
 Draw polygons

For example, the following code draws an eight-sided polygon with a fill pattern. The fill pattern
accepted by the glPolygonStipple function must be an array of 128 8-bit bitmaps. These bitmaps
represent a 32x32 pixel mask to be used for filling. The image produced by the code below is
shown in Figure 55.

GLubyte fillPattern[] = {0x00, 0xFF, 0x00, ... };


glPolygonStipple(fillPattern);
glEnable(GL_POLYGON_STIPPLE);
glBegin(GL_POLYGON);
glVertex2i(350,250);
glVertex2i(400,300);
glVertex2i(400,350);
glVertex2i(350,400);
glVertex2i(300,400);
glVertex2i(250,350);
glVertex2i(250,300);
glVertex2i(300,250);
glEnd();

Figure 55 - Example of Using the OpenGL Polygon Stipple Feature

KC Instr.H.Kedir Dept of cs Page 71


Summary

The following points summarise the key concepts of this chapter:


 Attributes of graphics primitives are parameters that affect the way the primitive is
displayed.
 Most colours are stored as RGB (red, green blue) or RGBA (red, green, blue, alpha) values.
 The alpha value of a colour indicates the degree of transparency of the colour.
 Colours can be stored by raster graphics systems using either direct storage or a colour
look-up table.
 Using direct storage the colour is stored directly in the frame buffer.
 Using a colour look-up table the colour is stored in a separate table and the frame buffer
stores an index into this table.
 The glColor* and glClearColor functions are used in OpenGL to set the drawing and
background colours respectively.
 Colour blending allows us to mix one colour with another, often to achieve transparency
effects.
 The glBlendFunc OpenGL routine allows us to define how colour blending occurs.
 The glPointSize function allows us to change the size of point primitives in OpenGL.
 Attributes of line primitives include the line width, line colour and line style.
 Line caps are commonly used to improve the appearance of the ends of plotted lines.
 Three common types of line cap are the butt cap, the round cap and the projecting square
cap.
 Line joins are commonly used to improve the appearance of polylines.
 Three common line joins are the miter join, the bevel join and the round join.
 The glLineWidth OpenGL function allows us to change the width of subsequently drawn
line primitives.
 The glLineStipple function allows us to change the style of lines by specifying a pixel mask.
 Scan-line fill algorithms fill enclosed areas automatically without user intervention. They
are commonly used by general purpose graphics packages such as OpenGL.
 Seed-based fill algorithms fill enclosed areas by starting from a user-specified seed-point,
and painting outwards until they reach a boundary.
 Boundary-fill and flood-fill are two common seed-based fill algorithms.
 The glPolygonMode OpenGL routine defines whether polygons should be filled, displayed
in wireframe, or displayed as just vertices.
 The glPolygonStipple function allows us to specify fill patterns for fill-area polygons in
OpenGL.
 The attributes of character primitives include the size, font/style and colour of the character.
 For stroke characters, we can change the thickness of characters using the glLineWidth
function in OpenGL. We can change the style of the lines in the characters using
glLineStipple.

KC Instr.H.Kedir Dept of cs Page 72


Part IV :- Transformations
1. Introduction

In this handout we will introduce some important theoretical foundation for the next chapter on
the viewing pipeline. We will review the mathematics of matrix transformations, and see how
matrices can be used to perform different types of transformation: translations, rotations and
scalings. We will introduce the important topic of homogeneous coordinates – a variation on the
standard Cartesian coordinate system that is widely employed in computer graphics. Finally we
will consider how transformations can be defined in OpenGL.

2. 2-D Matrix Transformations

First of all let us review some basics of matrices. 2x2 matrices can be multiplied according to the
following equation.

 a b  e f   ae  bg af  bh 
    …………………………………… (1)
 c d  g h   ce  dg cf  dh 

For example,

 3  1 1 2   3  1  1 2 3  2  1 0   1 6 
        
 2 1  2 0   2  1  1 2 2  2  1 0   4 4 

Matrices of other sizes can be multiplied in a similar way, provided the number of columns of the
first matrix is equal to the number of rows of the second.

Matrix multiplication is not commutative. In other words, for two matrices A and B, AB≠BA. We
can see this from the following example.

 1 2  3  1  5 1
     
 2 0  2 1   6  2 
 3  1 1 2   1 6
     
 2 1  2 0   4 4 

However, matrix multiplication is associative. This means that if we have three matrices A, B and
C, then (AB)C = A(BC). We can see this from the following example.

 3  1 1 2  1 0   1 6  1 0  13 6 


          
  2 1   2 0    2 1   4 4   2 1   12 4 

KC Instr.H.Kedir Dept of cs Page 73


 3  1  1 2  1 0   3  1 5 2  13 6 
           
 2 1   2 0  2 1   2 1  2 0  12 4 

In the following sections we will consider how to perform certain common types of coordinate
transformations using matrices. We will start off by looking only at 2-D points, i.e. points that
have an x and a y coordinate. Later in this chapter we will extend our discussion to 3-D points.

2.1. 2-D Translation

The translation transformation shifts all points by the same amount. Therefore, in 2-D, we must
define two translation parameters: the x-translation tx and the y-translation ty. A sample translation
is illustrated in Figure 56.

To translate a point P to P’ we add on a vector T:

 px 
P    ………………………………………………………………….. (2)
 py 
 px 
P    ………………………………………………………………….. (3)
 py 
 tx 
T    …………………………………………………………………... (4)
ty 

 px   px   t x 
      ………………………………………………………….. (5)
 p   p   t 
 y  y  y

Therefore, from Eq. (5) we can see that the relationship between points before and after the
translation is:

px  px  t x ………………………………………………………………….. (6)


py  py  t y ………………………………………………………………….. (7)

Figure 56 - A 2-D Translation

KC Instr.H.Kedir Dept of cs Page 74


2.2. 2-D Rotation

The rotation transformation rotates all points about a centre of rotation. Normally this centre of
rotation is assumed to be at the origin (0,0), although as we will see later on it is possible to rotate
about any point. The rotation transformation has a single parameter: the angle of rotation, θ. A
sample rotation in 2-D about the origin is illustrated in Figure 57.

To rotate a point P anti-clockwise by θo, we apply the rotation matrix R:

 cos  sin 
R    …………………………………………………………. (8)
 sin cos 
 px   cos  sin  px 
      …………………………………………. (9)
 p 
 y   sin cos  p y 

Therefore, from Eq. (9) we can see that the relationship between points before and after the rotation
is:

px  px cos  py sin ………………………………………………….. (10)


py  p y cos  p x sin ………………………………………………….. (11)

Figure 57 - A 2-D Rotation about the Origin

2.3. 2-D Scaling

The scaling transformation multiplies each coordinate of each point by a scale factor. The scale
factor can be different for each coordinate (e.g. for the x and y coordinates). If all scale factors are
equal we call it uniform scaling, whereas if they are different we call it differential scaling. A
sample scaling is shown in Figure 58.

KC Instr.H.Kedir Dept of cs Page 75


To scale a point P by scale factors Sx and Sy we apply the scaling matrix S:

 Sx 0 
S    ………………………………………………………………….
 (12)
 0 Sy 
 px   S x 0  px 
    …………………………………………………. (13)
 p   0 S  p 
  
y y  y 

Therefore, from Eq. (13) we can see that the relationship between points before and after the
scaling is:

px  S x px …………………………………………………………………. (14)


py  S y py …………………………………………………………………. (15)

Figure 58 - A 2-D Scaling

3. Homogeneous Coordinates

In the next chapter we will look at the viewing pipeline for computer graphics: a sequence of
transformations that every primitive undergoes before being displayed. As we must perform a
sequence of transformations in this pipeline, it is essential that we have an efficient way to execute
these transformations.

One answer is to compose the series of transformations into a single matrix, and then apply the
composed matrix to every primitive. This would be efficient because we perform the composition
once only (the transformation will be the same for all primitives), leaving only a single matrix
multiplication for each primitive. Since matrix multiplications are often executed in dedicated
hardware on the video card this will be very fast.

KC Instr.H.Kedir Dept of cs Page 76


There is a problem with this solution, however. We can illustrate this problem by looking at the
following two examples.

Example 1

We want to transform a large number of points by the same sequence of three matrix
transformations: a rotation R1, followed by a scaling S1 and finally another rotation R2. In this case,
the overall transformation is P  R2 S1R1P . Therefore we can implement this by composing R2, S1
and R1 into a single composite matrix C, and then multiplying each point by C.

Example 2

We want to transform a large number of points by the same sequence of four matrix
transformations: a translation T1, a rotation R1, another translation T2 and finally a scaling S1. In
this case, the overall transformation can be expressed as
P  S1 R1 P  T1   T2   S1R1P  S1R1T1  S1T2 . Clearly this is significantly more complex than the

previous example. Even if we combined S1R1, S1R1T1 and S1T2 into composite matrices we would
still have to apply two extra matrix additions for every point we wanted to transform. Therefore
the operation of the graphics pipeline would be much slower in this second example compared to
the first. The reason is that this second sequence of transformations included translations, whereas
the first sequence consisted of only rotations and scalings. Using the definitions we have seen so
far rotations and scalings are performed using matrix multiplications, whereas translations are
performed using matrix additions. We could improve the efficiency of the graphics pipeline if we
could find a way to express translations as matrix multiplications.

Homogeneous coordinates allow us to do just this. With homogeneous coordinates we add an extra
coordinate, the homogenous parameter, to each point in Cartesian coordinates. So 2-D points are
stored as three values: the x-coordinate, the y-coordinate and the homogeneous parameter. The
relationship between homogeneous points and their corresponding Cartesian points is:

 x  x/h
   
Homogeneous point =  y  , Cartesian point =  y / h
h  1 
   

Normally the homogenous parameter is given the value 1, in which case homogenous coordinates
are the same as Cartesian coordinates but with an extra value which is always 1. In the following
sections we will see how adding this extra homogeneous parameter helps us to express translation
transformations using matrix multiplications.

KC Instr.H.Kedir Dept of cs Page 77


3.1. 2-D Translation with Homogenous Coordinates

Now we can express a translation transformation using a single matrix multiplication, as shown
below.

 px 
 
P   py  …………………………………………………………………. (16)
1
 
 px 
 
P   py  …………………………………………………………………. (17)
1
 
1 0 tx 
 
T  0 1 ty  …………………………………………………. (18)
0 1
 0 

 px   1 0 t x  px 
    
 py    0 1 t y  p y  ………………………………………………… (19)
 1   0 0 1  1 
    

Therefore px  px  t x , py  py  t y , exactly the same as before, but we used a matrix
multiplication instead of an addition.

3.2. 2-D Rotation with Homogenous Coordinates

Rotations can also be expressed using homogenous coordinates. The following equations are
similar to the form of the 2-D rotation given in Eqs. (8)-(11), with the exception that the rotation
matrix R has an extra row and and extra column.

 cos  sin 0
 
R   sin cos 0 ………………………………………………….. (20)
 0 1 
 0
 px   cos  sin 0  px 
    
 py    sin cos 0  p y  ………………………………………….. (21)
1  0 1  1 
   0

Therefore px  px cos  py sin and py  py cos  py sin , which is the same outcome as
before.

KC Instr.H.Kedir Dept of cs Page 78


3.3. 2-D Scaling with Homogenous Coordinates

Finally, we can also express scalings using homogeneous coordinates, as shown by the following
equations.

 Sx 0 0
 
S  0 Sy 0 ………………………………………………………….. (22)
0 1 
 0
 px   S x 0 0  px 
    
 py    0 Sy 0  p y  ………………………………………………….. (23)
1 0 1  1 
   0

Therefore px  S x px and py  S y py , exactly the same as before.

4. Matrix Composition

As we saw in Section 3, the use of homogenous coordinates allows us to compose a sequence of


transformations into a single matrix. This can be very useful in the graphics viewing pipeline (see
next chapter), but also allows us to define different types of transformation from those we have
already seen. For example, the rotation matrix we introduced in Section 3.2 only rotates about the
origin, but often we may want to apply a rotation about a different point (a pivot point). Using
matrix composition, we can achieve this using the following sequence of transformations:
 Translate from pivot point to origin
 Rotate about origin
 Translate from origin back to pivot point

An example of this sequence of transformations is shown in Figure 59. Here we perform a rotation
about the pivot point (2,2) by translating by (-2,-2) to the origin, rotating about the origin and then
translating by (2,2) back to the pivot point. Let us denote our transformations as follows:
 T1 is a matrix translation by (-2,-2)
 R is a matrix rotation by θo about the origin
 T2 is a matrix translation by (2,2)
Therefore, using homogenous coordinates we can compose all three matrices into one composite
transformation, C:

C = T2RT1 ………………………………………………………………….. (24)

The composite matrix C can now be computed from the three constituent matrices T2, R and T1,
and represents a rotation about the pivot point (2,2) by θo. Note from Eq. (24) that T1 is applied
first, followed by R and then T2. For instance, if we were to apply the three transformations to a
point P the result would be

KC Instr.H.Kedir Dept of cs Page 79


P’ = T2RT1P.

Therefore because T1 is right next to the point P it gets applied first, followed by the next
transformation to the left, R, and so on.

Figure 59 - Composing Transformations to Achieve Rotation about an Arbitrary Pivot


Point

5. 3-D Matrix Transformations

The concept of homogenous coordinates is easily extended into 3-D: we just introduce a fourth
coordinate in addition to the x, y and z-coordinates. In this section we review the forms of 3-D
translation, rotation and scaling matrices using homogeneous coordinates.

5.1. 3-D Translation with Homogenous Coordinates

The 3-D homogeneous coordinates translation matrix is similar in form to the 2-D matrix, and is
given by:

1 0 0 t 
 
0 1 0 ty 
T 
0 0 1 tz  ………………………………………….. (25)
 
0 1 
 0 0

KC Instr.H.Kedir Dept of cs Page 80


We can see that 3-D translations are defined by three translation parameters: tx, ty and tz. We apply
this transformation as follows:

 px   1 0 0 t  px 
    
 py   0 1 0 t y  p y 
 p    0 0 1 t z  pz 
………………………………………………….. (26)
 z   
 1  0 0 1  1 
   0

Therefore px  px  t x , py  py  t y and pz  pz  t z .

5.2. 3-D Scaling with Homogeneous Coordinates

Similarly, 3-D scalings are defined by three scaling parameters, Sx, Sy and Sz. The matrix is:

 Sx 0 0 0
 
0 Sy 0 0
S 
0 0 Sz 0
 
0 1 
 0 0

We apply this transformation as follows:

 px   S x 0 0 0  px 
    
 py   0 Sy 0 0  p y 
 p    0 0 Sz 0  pz 
 z   
1 0 1  1 
   0 0

Therefore px  S x px , py  S y py and pz  S z pz .

5.3. 3-D Rotation with Homogenous Coordinates

For rotations in 3-D we have three possible axes of rotation: the x, y and z axes. (Actually we can
rotate about any axis, but the matrices are simpler for x, y and z axes.) Therefore the form of the
rotation matrix depends on which type of rotation we want to perform.

For a rotation about the x-axis the matrix is:

1 0 0 0
 
 0 cos  sin 0
Rx  
0 sin cos 0
 
0 1 
 0 0

KC Instr.H.Kedir Dept of cs Page 81


For a rotation about the y-axis we use:

 cos 0 sin  0
 
 0 1 0 0
Ry  
 sin  0 cos 0
 
 0 1 
 0 0

And for a rotation about the z-axis we have:

 cos  sin 0 0
 
 sin cos 0 0
Rz  
0 0 1 0
 
 0 1 
 0 0

5.4. Right-Handed vs. Left-Handed Coordinate Systems

When referring to 3-D coordinate systems, we can distinguish between right-handed and left-
handed coordinate systems. This concept is illustrated in Figure 60. For a right-handed coordinate
system, if we extend the thumb and first two fingers of our right-hand so that they are perpendicular
to each other, then the first finger represents the direction of the x-axis, the second finger the y-
axis and then thumb points in the direction of the z-axis. Contrast this to a left-handed coordinate
system in which we do the same thing with our left hand. In this case, if we align the x and y axes
of the right-handed and left-handed coordinate systems, the z-axes will point in opposite directions.
The most common type of coordinate system used in computer graphics is the right-handed
coordinate system, but when using a general purpose graphics package it is important to know
which type of coordinate system it uses.

Figure 60 - A Right-Handed Coordinate System

KC Instr.H.Kedir Dept of cs Page 82


6. Defining Transformations in OpenGL

Before we look at how to define matrix transformations in OpenGL we must introduce the concepts
of premultiplying and postmultiplying. Whenever a matrix is multiplied by another existing matrix
we can either premultiply or postmultiply. For example, if we premultiply matrix A by matrix B,
the result will be BA. If we postmultiply matrix A by matrix B the result will be AB. Often, when
using a general purpose graphics package we need to specify a sequence of transformations, so we
need to know whether the package will compose these transformations by premultiplying or
postmultiplying. This is very important because which of these two techniques the package uses
determines the order in which we should specify our transformations. For example, suppose we
specify a sequence of matrix transformations A, B and C (in this order). Using premultiplying, the
composite transformation will be CBA, whereas using postmultiplying it will be ABC. We have
already seen in Section 2 that matrix multiplication is not commutative, so these two results will
be different. We can see from this example that when postmultiplying we must define our sequence
of transformations in the reverse order to that in which we want them to be applied. The result of
postmultiplying the matrices A, B and C is ABC, so C is applied first, followed by B and then A.

Whenever we apply a transformation in OpenGL it is applied to a current matrix. In fact, as we


will see in the next chapter, in OpenGL we have several current matrices, but for the moment
just remember that there is a current matrix. Almost all transformations in OpenGL postmultiply
by this current matrix. Therefore when applying a sequence of transformations we must define
them in reverse order. We will see an example of this later. OpenGL always uses a right-handed
coordinate system

Now let us look at OpenGL functions for defining transformations. In total, there are six functions
that affect the current matrix:
 glTranslate*(tx,ty,tz): Postmultiply the current matrix by a translation matrix formed from
the translation parameters tx, ty and tz.
 glRotate*(θ,vx,vy,vz): Postmultiply the current matrix by a rotation matrix that rotates by
θo about the axis defined by the direction of the vector (vx,vy,vz).
 glScale*(Sx,Sy,Sz): Postmultiply the current matrix by a scaling matrix formed from the
scale factors Sx, Sy and Sz.
 glLoadMatrix*(elements16): Replaces the current matrix with the 16-element array
element16. The array should be defined in column-major order (i.e. the first four elements
represent the first column; the next four represent the second column, etc.).
 glMultMatrix*(elements16): Postmultiplies the current matrix with the 16-element array
element16. The array should be defined in column-major.
 glLoadIdentity(elements16): Replaces the current matrix with a 4x4 identity matrix.

Each current matrix in OpenGL has an associated matrix stack. This is a standard FIFO stack that
can be used to ‘remember’ different transformations. In fact, the current matrix is actually just the
top matrix on the matrix stack. We will see in the next chapter why matrix stacks can be useful,
but for the moment let us introduce the two functions for manipulating the stack:
 glPushMatrix: Copy the current matrix to the next position down in the stack, push all other
matrices down one position. The current matrix (i.e. the top matrix on the stack) is left
unchanged.

KC Instr.H.Kedir Dept of cs Page 83


 glPopMatrix: Destroy the current matrix, and move all other matrices on the stack up one
position.

To finish this chapter, let us look at an example of using these OpenGL routines to define a
composite transformation. Consider the following code:

glLoadIdentity();
glTranslated(2.0, 2.0, 0.0);
glRotated(90.0, 0.0, 0.0, 1.0);
glTranslated(-2.0, -2.0, 0.0);

This is actually the same example as we saw above in Figure 59: a rotation about the pivot point
(2,2). Note from this example that we define the transformations in reverse order (because OpenGL
always postmultiplies). This example uses 2-D graphics so the rotation is performed about the z-
axis.
Summary

The following points summarise the key concepts of this chapter:


 Matrix multiplication is not commutative, i.e. AB≠BA.
 Matrix multiplication is associative, i.e. (AB)C=A(BC).
 Using normal Cartesian coordinates, 2-D translation, rotation and scaling transformations
are defined as follows:
 px   px   t x 
o Translation:        
 py   p y   t y 
 px   cos  sin  px 
o Rotation:      
p 
 y  sin  cos  p y 
 px   S x 0  px 
o Scaling:      
 p 
p 
  
y 0 S y  y 
 Homogenous coordinates introduce an extra homogeneous parameter to standard Cartesian
coordinates. The homogenous parameter normally has the value 1.
 Using homogenous coordinates, 2-D translation, rotation and scaling transformations are
defined as follows:
 px   1 0 t x  px 
    
o Translation:  py    0 1 t y  p y 
 1   0 0 1  1 
    
 px   cos  sin 0  px 
    
o Rotation:  py    sin cos 0  p y 
1  0 1  1 
   0

KC Instr.H.Kedir Dept of cs Page 84


 px   S x 0 0  px 
    
o Scaling:  py    0 Sy 0  p y 
1 0 1  1 
   0
 Matrix composition refers to combining (i.e. multiplying together) a sequence of
transformations to produce a single transformation matrix.
 Using homogeneous coordinates, 3-D translation, rotation and scaling transformations are
defined as follows:
 px   1 0 0 t  px 
    
 py   0 1 0 t y  p y 
o Translation:    
p 0 0 1 t z  pz 
 z   
 1   0 0 0 1  1 
    
 px   S x 0 0 0  px 
    
 py   0 S y 0 0  p y 
o Scaling:    
p 0 0 S z 0  pz 
 z   
 1   0 0 0 1  1 
    
 px   1 0 0 0  px 
    
 py   0 cos  sin 0  p y 
o Rotation about the x-axis:    
p 0 sin cos 0  pz 
 z   
 1  0 1  1 
   0 0
 px   cos 0 sin 0  px 
    
 py   0 1 0 0  p y 
o Rotation about the y-axis:    
p  sin 0 cos 0  pz 
 z   
1  0 1  1 
   0 0

 px   cos  sin 0 0  px 


    
 p   sin cos 0 0  p y 
o Rotation about the z-axis:  y   
p 0 0 1 0  pz 
 z   
1  0 1  1 
   0 0

 Most graphics packages use a right-handed coordinate system: we can visualise the axes
of a right-handed coordinate system by extending the thumb and first two fingers of the
right hand so that they are perpendicular: the first finger is the x-axis, the second finger is
the y-axis and then thumb is the z-axis.
 If we premultiply matrix A by matrix B the result is BA. If we postmultiply A by B the result
is AB.

KC Instr.H.Kedir Dept of cs Page 85


The following routines can be used in OpenGL to define transformations:
o glTranslate*(tx,ty,tz)
o glRotate*(θ,vx,vy,vz)
o glScale*(Sx,Sy,Sz)
o glLoadMatrix*(elements16)
o glMultMatrix*(elements16)
o glLoadIdentity(elements16)
 OpenGL always postmultiplies by the current matrix. This means that the sequence of
transformations must be specified in the reverse order to that which we want them to be
applied.
 A matrix stack can be used in OpenGL to remember previous transformations. The
following routines can be used to manipulate the matrix stack:
o glPushMatrix
o glPopMatrix

Source: Computer Graphics with OpenGL,


Donald Hearn and M. Pauline Baker,
Prentice-Hall, 2004 (Third Edition)

KC Instr.H.Kedir Dept of cs Page 86

You might also like