CS 324: Computer Graphics Lecture Notes
Lecture 1
Lecture 2
Lecture 3
Lecture 4
Lecture 5
Lecture 6
Lecture 7
Lecture 8
Lecture 9
Lecture 10
Lecture 11
Lecture 12
Lecture 13
Lecture 14
Lecture 15
Lecture 16
Lecture 17
Lecture 18
Lecture 19
Lecture 20
Lecture 21
Lecture 22
Lecture 23
Lecture 24
Lecture 25
Lecture 26
Lecture 27
Lecture 28
Lecture 29
Lecture 30
Lecture 31
Lecture 32
Lecture 33
Lecture 34
Lecture 35
Lecture 36
lecture #1 began here
Pick up your supplemental text...
Please stop by JEB B26 to pickup your copy of the supplemental text.
Graphics is not just rendering
Computer graphics is a broad area of computer science, incorporating elements of:
graphic design
ray tracing and rendering
mathematical models of place
s and entities within them
graphics hardware, libraries, and window systems API's
user interfaces and graphical input
History
It would be stoopid for Dr. J to summarize the development of the entire field of computer
graphics in one bullet list, so her
e goes:
Before there was interactive computer graphics, there was non

interactive graphics with
several thousands of years of experience and expertise.
Ivan Sutherland invented a good part of modern interactive graphics in the 1960's for the
system called "Sketchpad" that was his Ph.D. thesis. Graphics was done using vector
displays and pen plotters; input with light pens.
Much of modern interactive graphics was invented at Xerox PARC in the 1970's,
especially bitmapped raster graphics, "windows", etc. Englebart invents mouse at
Stanford. Graphics bought on $30,000

200,000+ workstations by mil

industrial complex
giants and t
op architects/designers.
1980's were full of hot graphics developments:
o
Early PC's were monochrome and had no graphics capabilities. 12" is a common
screen size.
o
Competing lower

end machines offered easy color graphics (Radio Shack, Apple,
Commodore
, Atari, ...). 256x192 is a common screen size.
o
PC graphics becomes common on Hercules, EGA, and VGA graphics, and an
idyllic open system for developers spawns hundreds of small startups, many of
which produce spectacular new kinds of software and turn
young software
engineers fresh out of school into millionaires.
o
MIT introduces multi

platform portable graphics (X Window System). It was
kinda slow at first.
o
Microsoft and Apple majorly fail to adopt X, slowing progress of entire industry
several years
. X gets better slowly. A German undergraduate produces the first
"good" PC X server (UNIX/Linux).
o
Apple Macintosh was retro (tiny screen, monochrome) but successfully
popularized Xerox

style WIMP interface and the mouse input device.
o
Microsoft's Window
s seems to painfully imitate Mac.
Lawsuits ensue
.
o
Programmer documentation for Mac, Windows, and X11 all seem eerily derived
(at least in part) from some common Xerox ancestor (In fa
ct, Xerox did license
some UI features to Apple and Microsoft).
o
API's for graphics programming go from easy (Coco) to nightmarish. Partly the
new hardware capabilities challenge API designers, partly monopolies
deliberately act to extend their hegemonies.
Learning to program a Mac or
Windows box is a 6

12 month task.
o
Microsoft makes Windows programming difficult enough to wipe out its
competition in every app market it chooses to enter. "Undocumented Windows"
books explain what microsoft apps do that othe
rs can't. From now on, the main
way to become a young millionaire is to go work for Microsoft; BillG gets a piece
of all those young millionaires' action.
o
Silicon Graphics corporation becomes a market leader in 3D workstations, with
particularly fine soft
ware. Big digital divide between haves and have

nots.
1990's were even fuller of hot graphics developments:
o
20" CRT becomes a popular $1000 option. 1024x768 is a good resolution; up to
1600x1200 is fairly common.
o
DOOM proves huge market for 3D immersive
(desktop VR) graphics.
o
Graphics hardware "accelerators" implement 2D and then 3D graphics in
hardware. A $200 add

on allows a PC's to do what $200,000 workstations did a
few years previous.
o
OpenGL becomes the first widely adopted 3D standard API. Micr
osoft pledges
eternal allegiance in order to kill the workstation industry, especially market
leaders SGI and Sun.
o
Java capitalizes on need for portable graphics
2000

2008 are simply screaming:
o
3D Graphics chips (GPU's) outpace CPU's in rate of prog
ress.
o
Big video memories, "true color" displays are ubiquitous.
o
Researchers work on how to use GPU's to speedup general

purpose computation.
o
Fixed pipeline model gives way to programmable shader model and shading
languages.
o
Motion picture industry switches all animation to computers; CG achieves lifelike
detail in non

real

time on giant server farms
o
Videogame industry chases behind with very detailed, real

time on a single PC.
o
24" LCD becomes a popular $500 option.
o
Multipl
e monitors, and resolutions > 1600x1200 become common.
o
Microsoft quietly tries to derail OpenGL and own the standard (D3D, DirectX)
o
Two vendors dominate hardware (NVIDIA and ATI). NVIDIA embraces Linux
and OpenGL. ATI is eaten by AMD.
o
4x3 aspect ratio
gives way to 16x9 aspect ratio, spurred by HDTV
lecture #2 began here
When you read chapter #1...
Be sure to pay good attention to section 1.3.
Rationale for the
UIGUI
Library
Section 2.1.3 of your book gives an INCOMPLETE "hello world"

style program for getting a
basic drawing canvas working using OpenGL and the evil "glut". As written (incomplete) it is 14
long, ugly lines of glut calls, with no opengl. It is missing the #incl
udes and four functions you
must write. By the way, OpenGL does not include support for fonts, so "hello, world" will take a
bit more effort than you think.
My past experience with glut has been poor. Also, it is not really part of OpenGL

various
platf
orms that provide OpenGL do not provide glut. They all provide "gl" and "glu", the OpenGL
utilities library. So the text uses glut and we will use UIGUI ("oowee gooey", standing for "UI
GUI"). It is fine if you want to learn GLUT as well; go right ahead. W
e
will
be learning a lot of
"gl" and "glu" in a few weeks.
GLUT
uigui
// #includes
void main(ing arc, char **argv)
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE);
glutInitWindowSize(640,480);
glutInitWindowPosition(100,150);
glutCreateWindow("my first attempt");
glutDisplayFunc(myDisplay);
glutReshapeFunc(myReshape);
glutMouseFunc(myMouse);
glutKeyboardFunc(myKeyboard);
}
#include "uigui.h"
int main()
{
WOpen();
GotoRC(10,2);
writes("Hello, world");
WEnd();
}
Homework #1
Note that it is due a week from today.
Graphics Hardware
Graphics hardware has been developing at a very rapid pace, perhaps faster than Moore's Law.
This did not come about due to the civilian population benefitting from the research dollars spent
by our military. The driving application domain: computer games.
Without games, graphics on
PC's would still be primeval. If you are not a gamer, please put up with gamer references in this
class; if you use or profit from computer graphics, you owe the game industry at this point.
Frame Buffers
Bitmapped raster graphics is founded on the notion of some big hunk of physical memory being
mapped directly to pixels. It is common to use 1,4,8,15,16,24, or 32 bits per pixel. Writing
directly to memory is fast. Coding tends to be a mess of bitwise opera
tions.
Generally the operating system and/or window system limit access to the frame buffer. There is
conflict between abstraction and performance. 3D graphics "accelerator" hardware capabilities
are increasingly "higher level", to where writing to the fr
ame buffer yourself makes less and less
sense.
Graphics API's
Vendor specific API's have tended to be complex (400

800 functions, 50

100 complex structure
types). Portable graphics API's were slow and not widely accepted, more or less until the
OpenGL AP
I was developed. Reputable sources have claimed 3

6 months' training are required
to achieve reasonable mastery of a major API.
Implementing DrawPixel(w,x,y)
In this course we consider not just the application programmer's point of view, but the graphics
package implementor's point of view. Many of the fundamental algorithms implemented within
existing graphics packages are presented to enhance your understanding and use of these
packages.
Homework #0 postulated the use of a primitive for setting an indi
vidual dot (pixel, picture
element) on the screen, and the previous lecture described the frame buffer used to store the
contents of the screen in memory. So, how do we implement DrawPixel() ? The following
pseudocode fragments approximate it for a display
WIDTH x HEIGHT whose frame buffer is an
array of bytes for which we have a pointer named
fb
. Implementation for real hardware would
vary depending e.g. on whether the processor was "big

endian" or "little

endian", whether a 1

bit
means black or means whit
e, etc.
Monochrome (1

bit per pixel) case
void DrawPixel(int x, int y, int value)
{
int index = y * HEIGHT + x / 8;
int bit = y * HEIGHT + x % 8;
if (value) { /* set the bit on */
fb[index] = 1 << bit;
}
else { /* set the bi
t off */
fb[index] &= ~(1 << bit);
}
}
True Color (packed 24

bits per pixel) case
void DrawPixel(int x, int y, int value)
{
int index = (y * HEIGHT + x) * 3;
fb[index++] = value & 255;
fb[index++] = (value>>
8) & 255;
fb[index] = (value>>16) & 255;
}
Note that memory is cheap and writing 32

bits at a time has compelling advantages, so many
frame buffers are organized as 32

bits per pixel instead of 24, even though they only offer 24 bits
of colo
r. Some use the extra 8 bits, others ignore them.
lecture #3 began here
Happy Labor Day
Try to remember not to come to class this coming Monday. See you next Wednesday. Hint: do
not put off your homeworks until the night before they are due!
UIGUI stat
us
No one has complained yet, but I won't believe that this library will work for Anyone But Me
until I start hearing reports. Do not wait for classtime, send me e

mail with positive or negative
experiences. Today's update: Win32 and Linux 64

bit librarie
s now available; demo of same.
Line Drawing
OK, so we can draw pixels. How do we get from there to more complex graphic objects, such as
lines, rectangles, etc.? We need algorithms. 15 years ago graphics packages implemented these
algorithms in software
the same way you and I would, but nowadays most graphics hardware
implements many graphics algorithms directly. Pseudocode here switches to Icon as a way of
passively introducing the notation. It is inconvenient to postulate a window, and we are
pretending
we are writing to the framebuffer, so let us say that the window is a global (default)
variable.
Draws a horizontal line from (x1, y) to (x2, y):
while loop
"for"

loop
generator
x := x1
while x <= x2 do {
DrawPoint(x,y)
x := x + 1
}
every x :=
x1 to x2 do {
DrawPoint(x,y)
}
every DrawPoint(x1 to x2, y)
Brute force (incremental) line algorithm (based on: y = mx + B)
# note: use real numbers and round off, to avoid numeric errors
procedure DrawLine0(x1,y1,x2,y2)
m := real(y2

y1) / real(x2

x1)
y := real(y1)
every x := x1 to x2 do {
DrawPoint(x, integer(y + 0.5)) # round off
y +:= m
}
end
Midpoint algorithm (Bresenham, Pitteway, Van Aken)
Another important line algorithm was d
eveloped in the 60's and subsequently refined.
Starting from the form F(x,y) = ax + by There is some cleverness here, to avoid any need
for floating point numbers, which speeds things up greatly, especially on machines that
do not have floating point instr
uctions!
procedure DrawLine1(x1,y1,x2,y2)
dx := x2

x1
dy := y2

y1
d := 2 * dy

dx
incrE := 2 * dy
incrNE := 2 * (dy

dx)
x := x1
y := y1
DrawPoint(x,y)
while x < x2 do {
if d <= 0 th
en {
d +:= incrE
x +:= 1
}
else {
d +:= incrNE
x +:= 1
y +:= 1
}
DrawPoint(x,y)
}
end
Midpoint Line Algorithm Example
The best way to get a feel for Bresenham/Midpoint in action is to see it. Here are the
incrE and incrNE, and the value of x, y, and d for the line segment from (50,50) to
(60,63):
incrE 6 incrNE

14
x 50 y 50 d

4
x 51 y 50 d 2
x 52 y 51 d

12
x 53 y 51 d

6
x 54 y 51 d 0
x 55 y 51 d 6
x 56 y 52 d

8
x 57 y 52 d

2
x 58 y 52 d 4
x 59 y 53 d

10
x 60 y 53 d

4
API parameters and graphic contexts
From skimming our text you should notice that a host of attributes (line width and style,
color, etc) are applied in any given graphic output command. Some API's use separate
parameters for each such "dimension", leading to very large parameter lists. In t
he design
of the X Window System, this was avoided in order to reduce network traffic. Instead,
resources for drawing attributes are preallocated on the display server in a large data
structure called a graphics context. The X graphics context is not entir
ely complete (e.g.
it doesn't include fonts) and other API's subdivide the graphics context further into
specific abstractions such as "pen" and "brush".
Color Indices and Colormaps
So far we have mainly considered monochrome and "true color" frame buffe
rs. 15

and
16

bits per pixel color displays are available that closely approximate "true color" in
behavior. You should learn of at least one very different method of organizing a frame
buffer, commonly used for 4

and 8

bits per pixel color displays. Ins
tead of literally
representing intensity of color, the frame buffer bits on these displays are often used as
subscripts into an array (called a colormap) whose elements are color intensities. The
"array" may be hardwired to a specific set of colors, or mod
ifiable. If it is modifiable, the
colormap provides a level of indirection that enables clever (and non

portable) tricks to
be played, where modifying a single array element instantaneously changes the color for
many (possibly hundreds of thousands) pixels
.
Circle Drawing
Circles obey the an equation such as x
2
+ y
2
= R
2
. If you can draw a good quarter circle,
you can draw the rest of the circle by symmetry. If you try to draw a quarter circle by
advancing x each step and solving the equation for y (y ==
+

sqrt(R
2

x
2
) from the
earlier equation. But besides being slow, for part of the circle pixels will not touch
because the slope is too steep, similar to the problem we saw drawing very "steep" lines
looping on the x coordinate.
To avoid the discontinui
ty you can write a loop that steps through small angular
increments and uses sin() and cos() to compute x and y.
Brute force circle algorithm (based on: y == +

sqrt(R^2

x^2))
How small an angular increment is needed? our distance is 2 * pi * R pixels
worth of
drawing; we need angular increments R small enough to make these adjacent. 1/R radians
gives a nice solid circle; angular steps twice as large still look like a connected, thinner
circle.
procedure DrawCircle0(x,y,R)
every angle := 0.0 t
o 2 * &pi by 1.0 / R do {
px := x + sin(angle) * R
py := y + cos(angle) * R
DrawPoint(px,py)
}
end
8 Way Symmetry
Symmetry can speed up and simplify computations. We can do 1/8 as many calls to sin(),
cos(), etc. This code also relies on "translation" of coordinates from (0,0) to an origin of
(x0,y0). Translation is the first of many coordinate transformations you wil
l see.
procedure CirclePoints(x0, y0, x, y)
DrawPoint(x0 + x, y0 + y)
DrawPoint(x0 + y, y0 + x)
DrawPoint(x0 + y, y0

x)
DrawPoint(x0 + x, y0

y)
DrawPoint(x0

x, y0

y)
DrawPoint(x0

y, y0

x)
DrawPoint(
x0

y, y0 + x)
DrawPoint(x0

x, y0 + y)
end
Mid

point (or Bresenham) circle algorithm
Like the mid

point line algorithm, it is possible to do a much more efficient incremental
job of drawing a circle. See Fig 3.14. The procedure is very similar
to the Mid Point line
algorithm.
procedure DrawCircle1(x0,y0,R)
x := 0
y := R
d := 5.0 / 4.0

R
CirclePoints(x0, y0, x, y)
while (y > x) do {
if d < 0 then {
d +:= 2.0 * x + 3.0
}
else {
d +:= 2.0 * (x

y) + 5.0
y

:= 1
}
x +:= 1
CirclePoints(x0, y0, x, y)
}
end
lecture #4 began here
Discuss
Homewo
rk #1
and Check Out
HW #2
Restating the Homework Platforms
For doing graphics homeworks, your options are:
1. download and install the necessary graphics libraries and header files to a local
machine.
To run locally, you will have to download an appropriate libuigui.a and uigui.h in order
for your C programs to compile. In fact, you will have to do this a few
times this semester
(as libuigui gets "fleshed out". In order to use icon/unicon you would similarly need to
download and install unicon from unicon.org, but that is not required for CS 324.
2. ssh into a CS machine.
For this option, the recommended mach
ine is wormulon.cs.uidaho.edu. The ssh must be
told to support X11 graphics connections. The command to do this is "ssh

X" from
Linux. From Windows the correct command to do X window forwarding depends on
which ssh client you use, but on the one from ssh.
com you would go into the Edit menu,
choose the Settings dialog, click open the Profile Settings, Connection, and Tunneling
tabs, and click the "Tunnel X11 connections" checkbox. From what I have seen, this
option works reasonably well for students at pres
ent, and allows me to update libraries
without you having to download them again. But once we get to assignments where
performance matters, being able to run locally will become a big advantage.
Clarifications on Bresenham/Midpoint Algorithms from Last Le
cture
See the book, pages 467

472 for another explanation of Bresenham's algorithm.
See the
wikipedia article on Bresenham's circle algorithm
for a better description of that
algorithm, using integer

only operations. An example program
circ.icn
is worth playing
with.
Raster Operations
Our hardware lesson for today is ye
t another nuance of frame buffer manipulation.
Consider the frame buffer as an array of bits in memory. When you are "drawing" on the
frame buffer, you are writing some new bits out to that memory. You have the option of
just replacing and discarding what
used to be there, but there are other possibilities that
involve
combining
the new value with the old. Each bit of the current contents could be
combined using AND, OR, and NOT with corresponding bits being output. These
different boolean combinations of c
urrent frame buffer with new output pixel values are
called raster operations.
The most interesting raster operation is probably XOR. It has the property that XOR'ing a
pixel value a second time restores the pixel to its former contents, giving you an eas
y
"undo" for simple graphics. On monochrome displays XOR works like a champion, but
on color displays it is less useful, because the act of XOR'ing in a pixel value may
produce seemingly random or indistinct changes, especially on color index displays
wher
e the bits being changed are not color intensities. If a color display uses mostly a
foreground color being drawn on a background color, drawing the XOR of the
foreground and background colors in XOR mode works pretty much the way regular
XOR does on monoc
hrome displays.
Major Concepts from Hill/Kelley Chapter 2
Note: chapter 2 contains a bunch of introduction to OpenGL, covering various functions,
mainly from the gl and glut libraries. In some sense it is profoundly overcomplicated.
Absorb what you can,
and ask what you want, while we use our much simpler library for
awhile.
absolute vs. relative coordinates
compare DrawPoint(x1,y1,x2,y3) with DrawTo(xNew, yNew), where xOld and yOld are
an implicit "current drawing position".
window coordinates vs. scr
een coordinates
pixel coordinates are generally relative to a current window...
normalized coordinates
going from (0.0,0.0) to (1.0,1.0) gives device independence
aspect ratio
width/height matters. For example drawings in normalized coordinates may lo
ok funny if
computed with different aspect ratio than displayed.
event

driven programming
give up on your ownership of control flow, for most of the semester, you write function
bodies that get called when the graphics system feels like it. In UIGUI this
is "optional"
but in OpenGL and most GUI class libraries it is mandatory. Creates problems for
novices (need to "think backwards"; can't use program counter to remember where you
are any more). Remember where you are by using a state machine (state variab
le(s)). For
your functions to get called by the graphics system, you have to tell the system about
them (register).
lecture #5 began here
Here's the
CS Lab FAQ
Check it out; it provides some useful information on our current labs.
An Overview of UIGUI's Graphics Primitives
Note: uigui.h and libuigui.a were updated last night for all platforms to add the
DrawLine() primitive. More to come!
Icon/Unicon 2D facili
ties consist of around 45 functions in total. There is one new data
type (Window) which has around 30
attributes
, manipulated using strings. Most graphics
API's introduce a few hundred functions and several dozens of new typedefs and struct
types that you
must memorize. UIGUI will eventually have around 90 if each of the 45
requires two C versions.
These facilities are mainly described in Chapters 3 and 4 of the Icon Graphics Book. Here
are some of my favorite functions you should start with. The main differences between
Icon/Unicon and UIGUI: the C functions are not able to take an optional initial
parameter, and do not yet support multiple primitives with a single call. This is less
useful in C than in Icon anyhow, since C does not have an "apply" operator. An initial
"working set" of UIGUI primitives will consist of just under half of the whole 2D
API:
WOpen(), WClose()
Open and close the default window. Stickiness around the issue of initial attributes.
WAttrib(), Fg(), Bg()
Get/set an attribute. Fg() and Bg() are common special cases
DrawPoint(), DrawLine()
We have seen these by now.
{Draw,
Fill}{Arc,Circle,Polygon,Rectangle}
8 functions for other common 2D primitives. In Icon; forthcoming in UIGUI. Built

ins in
underlying Xlib/Win32/hardware.
DrawCurve()
A smooth curve algorithm with nice properties. In Icon; forthcoming in UIGUI.
Impleme
nted in software.
Event()
read a keyboard OR mouse event. In another lecture or two we will get around to
discussing the extra information available about an event beyond its return value "code".
Especially: mouse location.
Pixel()
Read the contents of
a screen pixel (in Icon: a rectangular region). In Icon; forthcoming
in UIGUI.
Region Filling
Suppose you want to fill in a circle or rectangle you've just drawn using one of the last
few algorithms we've discussed? Starting from a point inside the obje
ct to be filled, one
can examine pixels, and set all of them to a new color until one hits the border color of
the object.
The border that defines a region may be 4

connected or 8

connected. 4

connected regions
do not consider diagonals to be adjacent; 8

connected regions do. The following
algorithms demonstrate brute force filling. The foreground color is presumed to be set to
"new".
# for interior

defined regions. old must not == new
procedure floodfill_4(x,y,old,new)
if Pixel(x,y,1,1) === old then {
DrawPoint(x,y)
floodfill_4(x,y

1,old,new)
floodfill_4(x,y+1,old,new)
floodfill_4(x

1,y,old,new)
floodfill_4(x+1,y,old,new)
}
end
For the example we started with (filling in one of our circles) only slightly more is
need
ed:
# for boundary

defined regions. the new color may be == to the
boundary
procedure boundaryfill_4(x, y, boundary, new)
p := Pixel(x,y,1,1) # read pixel
if p ~== boundary & p ~== new then {
DrawPoint(x,y)
boundaryfill_4(x,y

1,boundary
,new)
boundaryfill_4(x,y+1,boundary,new)
boundaryfill_4(x

1,y,boundary,new)
boundaryfill_4(x+1,y,boundary,new)
}
end
The main limitation of these brute

force approaches is that the recursion is deep, slow,
and heavily redundant. One
way to do better is to fill all adjacent pixels along one
dimension with a loop, and recurse only on the other dimension.
More reading the frame buffer with Pixel()
If you try one of the fill algorithms above, they do genuinely recurse crazily enough to
get stack overflows. Another reason they are sooo slow is because of the X11 window
system architecture, which does not support reading the frame buffer as efficiently
as
writing to it; in particular, to ask for the contents of a window in general you must send a
network message and wait for a reply.
Pixel(x,y,w,h) asks for all the pixels in a rectangular region with a single network
transaction, which will be must fas
ter than reading each pixel individually. In general,
client

side "image" structures are not interchangeable with server

side "pixmap" and
"window" structures, but this is an unfortunate limitation of their design.
How, how do you store a local (client

si
de) copy of a window in order to work with it
efficiently? There are many ways you can represent an image, but we will start with a
couple of brute force representations using lists of lists and tables of tables.
lecture #6 began here
Where we are at in t
he Course
I've given you reading assignments in chapter 2, but about half of that chapter is gross,
low

level OpenGL details that I want to you to skim over, and half are important and
useful ideas and principles I want you to "get". This week we will plo
w on into Chapter
3.
Q&A
Q: What is the default window size under UIGUI?
A: 80 columns, 12 rows, in the system default "fixed" font.
Q: function watt() doesn't work, what up?
A: watt() supported most context attributes out of the box, but needed some
more code to
catch canvas attributes such as size=. Code has been added; new library build will come
shortly.
Parameterizing Figures
Textbook example 2.3.3 illustrates an important concept, which is to code generic
graphics and the apply them in different
situations using different parameters to modify
details. A 5

vertex house shape with a 4

vertex chimney are "parameterized" with a
translation (peak.x,peak.y) and a scale (given as a width and height). This is a precursor
of things to come (transformation
s, Hill chapter 5). If you push this "add flexibility to my
graphics code" theme farther you end up with "write generic code that reads all the data
from a file", another theme that Hill promotes. Anyhow, let's do a house shape using
normalized coordinates
and apply translation and scaling, as an exercise.
Convex Polygons
While we're discussing region filling, I mentioned that the 3D facilities (in OpenGL)
support FillPolygon, but the polygons must be convex. See your Hill textbook, page 69
for a discussi
on of this and a definition of convex polygons. We'll come back to the topic
in the 2nd half of the course.
Golden Rectangle
The Hill text page 86

87 has a discussion of the "golden rectangle". Following the golden
ratio, 1.618033989... Note that the HDT
V aspect ratio of 16:9 is closer to the golden ratio
than the old 4:3 aspect ratio, but it sure is wider/skinnier than the real thing.
Faster Fills
To speedup the fill algorithm, one can
1.
Eliminate recursion, replace it with list operations and loops
2.
Reduce the number of redundant checks on each pixel
3.
Think in terms of line segments instead of individual pixels when possible.
# nonrecursive boundary fill. the new color may be == to the boundary
procedure nr_boundaryfill_4(x, y, boundary, new)
L :
= [x, y]
while *L > 0 do {
x := pop(L)
y := pop(L)
p := Pixel(x,y,1,1) # read pixel
if p ~== boundary & p ~== new then {
DrawPoint(x,y)
put(L, x, y

1)
put(L, x, y+1)
put(L, x

1, y)
put(L, x+1, y)
}
}
end
Backing Store and Expose Events
Using UIGUI we operate under the illusion that each on

screen canvas is a chunk of the
frame buffer that we control as long as our program requires it. In reality, window
systems are sharing and coordinating use of the frame buffer much as operating system
s
schedule the CPU. In particular, moving, resizing, overlapping, minimizing and
maximizing windows all point out the fact that an application doesn't have exclusive or
continuous access to the frame buffer.
Under these various circumstances, window syste
ms may discard part or all of an
application's screen contents, and ask the application to redraw the needed portion of the
screen later, when needed. A window system tells an application to redraw its screen by
sending it an "expose" or "paint" event. Som
e applications just maintain a data structure
containing the screen contents and redraw the screen by traversing this data structure; this
is the raster graphics analog of the old "display list" systems used in early vector graphics
hardware. But clients a
re complicated unnecessarily if they have to remember what they
have already done. The obvious thing to do, and the thing that was done almost
immediately, was to add to the window system the ability to store screen contents in an
off

screen bitmap called
a backing store, and let it do its own redrawing. Unfortunately,
evil software monopolists at AT&T Bell Labs such as Rob Pike patented this technique,
despite not having invented it, and despite its being obvious. This leaves us in the
position of violatin
g someone's patent whenever we use, for example, the X Window
System.
Contexts and Cloning
Last lecture we introduced (via whiteboard) the concepts of graphics contexts, and
cloning the context. This is an expanded treatment of that. With certain limits,
you can
have multiple contexts associated with a canvas, or can use a context on multiple
canvases. Clone(w) creates an additional context on window w. Attributes in a context
include:
colors: fg, bg, reverse, drawop, gamma
text: font, fheight, ...
dra
wing: fillstyle, linestyle, linewidth, pattern
clipping: clipx, clipy, clipw, cliph
translation: dx, dy
In contrast, canvas attributes:
window: label, image, canvas, pos, posx, posy
size: resize, size, height, width, rows, columns
icon: iconpos, icon
label, iconimage
text: echo, cursor, x, y, row, col
pointer: pointer, pointerx, pointery, pointerrow, pointercol
screen: display, depth, displayheight, displaywidth
lecture #7 began here
Announcements
Class canceled; homework due date extended
Offhand, it looks like I will be unable to teach on Wednesday 9/10. HW#2 is due Friday.
MW next week you have guest lectures
My excellent doctoral student Jafar will tell you a bit about our graphics

related research.
He is a more bona

fide OpenGL guru t
han I, so check out his cool stuff.
lecture #8 began here
Check Out HW#3
"World Window" and "Viewport"
These Hill Ch. 3 concepts are important. The world window specifies what portion of a
scene is to be rendered. Things outside the world window must be
removed at some point
from the calculations of what to draw. The viewport defines where on the physical
display this world window is to appear. Rectangles are usually used for both of these, and
if their aspect ratios do not match, interesting stretching
may occur.
"World coordinates" are coordinates defined using application domain units. Graphics
application writers usually find it far easier to write their programs using world

coordinates. Within the "world", the graphics application presents one or mo
re rectangle
views, denoted "world

coordinate windows", that thanks to translation, scaling, and
rotation, might show the entire world, or might focus on a very tiny area in great detail.
A second set of transformations is applied to the graphics primitiv
es to get to the
"physical coordinates", or "viewport" of whatever hardware is rendering the graphics.
The viewport's physical coordinates might refer to screen coordinates, printer device
coordinates, or the window system's window coordinates.
If the worl
d coordinates and the physical coordinates do not have the same height

width
aspect ratio, the window

to

viewport transformation distorts the image. Avoiding
distortion requires either that the "world

coordinate window" rectangles match the
viewport rectan
gles' shapes, or that part of the viewport pixels go unused and the image is
smaller.
Fonts
"xlsfonts" reports 5315 fonts on the Linux system in my office. Windows systems vary
even more than X11 systems, since applications often install their own fonts.
Fonts have: height, width, ascent, descent, baseline. Font portability is one of the major
remaining "issues" in Icon's graphics facilities. There are four "portable font names":
mono, typewriter, sans, serif, but unless/until these become bit

for

bit ide
ntical, programs
that use text require care and/or adjustment in order to achieve portability. You can "fish
for a font" with something like:
Font(("Frutiger""Univers""Helvetica""sans")  ",14")
Proportional width fonts may be substantially more use
ful, but require substantially more
computation to use well than do fixed width fonts. TextWidth(s) tells how many pixels
wide string s is in a window's current font. Here is an example that performs text
justification.
procedure justify(allwords, x, y, w
)
sofar := 0
thisline := []
while word := pop(allwords) do {
if sofar + TextWidth(word) + (*thisline+1) * TextWidth(" ") > w
then {
setline(x, y, thisline, (w

sofar) / real(*thisline))
thisline := []
sofar := 0
y +:= WAttrib("fheight")
}
}
end
procedure setline(x,y,L,spacing)
while word := pop(L) do {
DrawString(x,y,word)
x +:= TextWidth(s) + spacing
}
end
Smooth Curves
DrawCurve(x1,y1,...,xN,yN) draws a smooth curve. If firs
t and last points are the same,
the curve is closed and is smooth around this boundary point. The algorithm used in
UIGUI comes from [Barry/Goldman 88] in the SIGGRAPH 88 conference proceedings.
Let's see how much we can derive from principles.
How do you draw smooth curves? You can approximate them with "polylines" and if you
make the segments small enough, you will have the desired smoothness property. But
how do you calculate the polylines for a smooth curve?
brute force
You can just represe
nt the curve with every point on it, or with very tiny polylines. This
method is memory intensive and makes manipulating the curve untenably tedious. The
remaining options represent the curve with mathematical function(s) that approximate the
curve.
expli
cit functions y=f(x)
does not handle curves which "double back"
implicit equations f(x,y)=0
hard to write equations for parts of curves, hard to sew multiple curves together smoothly
parametric equations x=x(t) y=y(t)
avoids problems with slopes; comp
lex curves are sewn together from simpler ones;
usually cubic polynomials are sufficient for 3D.
Sewing together curves is a big issue: the "slope" (endpoint tangent vectors are used to
track these) of both curves must be equal at the join point.
Splines
In the real world, a spline is a metal strip that can be bent into various shapes by pulling
specific points on the strip in some direction with some amount of force. The
mathematical equivalent is a continuous cubic polynomial that passes through a set
of
control points. There are lots of different splines with interesting mathematics behind
them. Catmull

Rom splines are one popular family of splines, with the property that the
slopes as the curve passes through each point will be parallel to the surroun
ding points
(Figure 11.32). Icon's DrawCurve() function uses the Catmull

Rom splines, specifically
the algorithm cited in CG as [BARR88b]: Barry, Phillip J., and Goldman, Ronald N.
(1988). A Recursive Evaluation Algorithm for a class of Catmull

Rom Splines
.
SIGGRAPH'88 conference, published in Computer Graphics 22(4), 199

204.
Smooth Curves

an algorithm
You stitch together the whole curve by doing one chunk at a time; that is, each outer step
of the algorithm draws the spline between two adjacent points
of the supplied vertices.
Within that outer step, there are guts to setup the matrices for this spline segment, and a
loop to plot the points.
Since we are drawing a smooth curve piecewise, to do the curve segment between p
i
and
p
i+1
you need four points actually; you need the points p
i

1
and p
i+2
in order to do the job.
The algorithm will step through the i's starting with i=4 (3 if you were C language going
0..3 as your first 4 points), and counting i that way as the point
after
the
two points whose
segment we are drawing, the steps will actually render the curve between p
i

2
and p
i

1
.
The data type used in the Icon implementation is a record Point with fields x and y.
Under X11 you could use XPoint and under MS Windows, POINT would
denote the
same thing (duh).
record Point(x,y)
There are interesting questions at the endpoints! The algorithm always needs some p
i

1
and p
i+2
so at the endpoints one must supply them somehow. Icon, Unicon, and UIGUI
use the following rule:
if p
1
== p
N
then
add p
N

1
before p
1
and p
2
after p
N
.
else
duplicate p
1
and p
N
at their respective ends.
Consequently, I have added this to the front of the gencurve() procedure for the lecture
notes.
# generate a smooth curve between a list of points
proce
dure gencurve(p)
if (p[1].x == p[

1].x) & (p[1].y == p[

1].y) then { # close
push(p, copy(p[

2]))
put(p, copy(p[2]))
}
else { # replicate
push(p,copy(p[1]))
put(p,copy(p[

1]))
}
Now to draw every segment between p
i

2
and p
i

1
. This is a "for" loop:
every i := 4 to *p do {
Build the coefficients ax, ay, bx and b_y, using: (note: b_y not "by", because by is an
Icon reserved word). This part is "magic": you have to lookup M
CR
and G
i
Bs
in the journal
article yourself.
_ _ _ _
i i 1 

1 3

3 1   Pi

3 
Q (t) = T * M * G =

 2

5 4

1   Pi

2 
CR Bs 2 

1 0 1 0   Pi

1 
_ 0 2 0 0_ _Pi _
Given the magic, it is clear how the ax/ay/bx/b_y are calculated:
ax :=

p[i

3].x + 3 * p[i

2].x

3 * p[i

1].x + p[i].x
ay :=

p[i

3].y + 3 * p[i

2].y

3 * p[i

1].y + p[i].y
bx :=
2 * p[i

3].x

5 * p[i

2].x + 4 * p[i

1].x

p[i].x
b_y := 2 * p[i

3].y

5 * p[i

2].y + 4 * p[i

1].y

p[i].y
Calculate the forward differences for the (parametric) function using parametric intervals.
These used to be intervals of size 0.1 along the total curve; that wasn't smooth enough for
large curves, so they were expanded to max(x or y axis difference). This
is a Bug! It is
not always big enough! What is the correct number to use??
steps := max(abs(p[i

1].x

p[i

2].x), abs(p[i

1].y

p[i

2].y))
+ 10
stepsize := 1.0 / steps
stepsize2 := stepsize * stepsize
stepsize3 := stepsize * step
size2
thepoints := [ ]
From here on out as far as Dr. J is concerned this is basic calculus and can be understood
by analogy to physics. dx/dy are velocities, d2x/d2y are accelerations, and d3x/d3y are
the changes in those accelerations... The 0.5's
and the applications of cubes and squares
are from the "magic matrix"...
x := p[i

2].x
y := p[i

2].y
put(thepoints, x, y)
dx := (stepsize3*0.5)*ax + (stepsize2*0.5)*bx +
(stepsize*0.5)*(p[i

1].x

p[i

3].x)
dy := (stepsize3
*0.5)*ay + (stepsize2*0.5)*b_y +
(stepsize*0.5)*(p[i

1].y

p[i

3].y)
d2x := (stepsize3*3) * ax + stepsize2 * bx
d2y := (stepsize3*3) * ay + stepsize2 * b_y
d3x := (stepsize3*3) * ax
d3y := (stepsize3*3) * ay
The "inner for loop" calculates the points for drawing (this piece of) the curve, broken
into steps.
every 1 to steps do {
x +:= dx
y +:= dy
dx +:= d2x
dy +:= d2y
d2x +:= d3x
d2y +:= d3y
put(thepoints, x, y)
}
DrawLine i
s used instead of DrawPoints in order to avoid holes and get working
linwidth/linestyle, but this turns out to be a mixed bag...
DrawLine ! thepoints
}
end
Smooth Curves

a slight embarassment
The following figures depict the behavior of the
smooth curves algorithm on the curve
through points. In each figure the top picture is the algorithm as given, and the bottom is
the built

in drawcurve, which is (should be) doing the same algorithm in C.
After my first pass, it was obvious some pixels seem to be missing, not just from the Icon
version, but from the "official" built

in version! Some missing pixels were filled in by
adding another line segment to the end of each step:
To fill in more pixels, I traced the actual execution behavior, and saw some pixels in the
generated output not showing up! Pixels in green are generated by the algorithm but not
shown in the drawn curve:
Ways to fix: make the stepsize smaller? Change
from DrawLine to DrawPoint as
DrawCurve's underlying primitive? Using lines instead of points was perhaps done in the
first place to avoid gaps in drawn output, but some API's (X11?) exclude the endpoints of
lines being drawn... However, DrawLine is potent
ially nicer than DrawPoint from the
standpoint that it will use the "linewidth" (handy) and "linestyle".
drawCurve() C implementation
This code is where the Icon code above came from, except for the begin

point and end

point duplication shown above and n
ot given here.
/*
* genCurve

draw a smooth curve through a set of points.
* Algorithm from Barry, Phillip J., and Goldman, Ronald N. (1988).
* A Recursive Evaluation Algorithm for a class of Catmull

Rom
Splines.
* Computer Graphics 22(4), 199

204.
*/
void genCurve(w, p, n, helper)
wbp w;
XPoint *p;
int n;
void (*helper)
(wbp, XPoint [], int);
{
int i, j, steps;
float ax, ay, bx, by, stepsize, stepsize2, stepsize3;
float x, dx, d2x, d3x, y, dy,
d2y, d3y;
XPoint *thepoints = NULL;
long npoints = 0;
for (i = 3; i < n; i++) {
/*
* build the coefficients ax, ay, bx and by, using:
* _ _ _ _
* i i 1


1 3

3 1   Pi

3 
* Q (t) = T * M * G =

 2

5 4

1   Pi

2 
* CR Bs 2 

1 0 1 0   Pi

1 
* _ 0 2 0 0_ _Pi _
*/
ax = p[i].x

3 * p[i

1].x + 3 * p[i

2].x

p[i

3].x;
ay = p[i].y

3 * p[i

1].y + 3 * p[i

2].y

p[i

3].y;
bx = 2 * p[i

3].x

5 * p[i

2].x + 4 * p[i

1].x

p[i].x;
by = 2 * p[i

3].y

5 * p[i

2].y + 4 * p[i

1].y

p[i].y;
/
*
* calculate the forward differences for the function using
* intervals of size 0.1
*/
#ifndef abs
#define abs(x) ((x)<0?

(x):(x))
#endif
#ifndef max
#define max(x,y) ((x>y)?x:y)
#endif
steps = max(abs(p[i

1].x

p[i

2].x), abs
(p[i

1].y

p[i

2].y)) +
10;
if (steps+4 > npoints) {
if (thepoints != NULL) free(thepoints);
thepoints = (XPoint *)malloc((steps+4) * sizeof(XPoint));
npoints = steps+4;
}
stepsize = 1.0/steps;
stepsize2 = stepsize * stepsize;
stepsize3 = stepsize * stepsize2;
x = thepoints[0].x = p[i

2].x;
y = thepoints[0].y = p[i

2].y;
dx = (stepsize3*0.5)*ax + (stepsize2*0.5)*bx +
(stepsize*0.5)*(p[i

1].x

p[i

3].x);
dy = (s
tepsize3*0.5)*ay + (stepsize2*0.5)*by +
(stepsize*0.5)*(p[i

1].y

p[i

3].y);
d2x = (stepsize3*3) * ax + stepsize2 * bx;
d2y = (stepsize3*3) * ay + stepsize2 * by;
d3x = (stepsize3*3) * ax;
d3y = (stepsize3*3) * ay;
/* calculat
e the points for drawing the curve */
for (j = 0; j < steps; j++) {
x = x + dx;
y = y + dy;
dx = dx + d2x;
dy = dy + d2y;
d2x = d2x + d3x;
d2y = d2y + d3y;
thepoints[j + 1].x = (int)x;
thepoints[j + 1].y = (int)y;
}
helper(w, thepoints, steps + 1);
}
if (thepoints != NULL) {
free(thepoints);
thepoints = NULL;
}
}
static void curveHelper(wbp w, XPoint *thepoints, int n)
{
/*
* Could use drawpoints(w, thepoints,
n)
* but that ignores the linewidth and linestyle attributes...
* Might make linestyle work a little better by "compressing"
straight
* sections produced by genCurve into single drawline points.
*/
drawlines(w, thepoints, n);
}
/*
* draw a smooth curve through the array of points
*/
void drawCurve(w, p, n)
wbp w;
XPoint *p;
int n;
{
genCurve(w, p, n, curveHelper);
}
Images
Image manipulation involves three areas
file formats
window system native (in X11, server side)
client

side image manipulation
File formats are numerous; these are things like GIF, JPG, BMP, PNG. They vary
according to compression size, lossiness, and portability. Icon does GIF on all platforms,
plus whatever formats are built

in to the Window syste
m (e.g. BMP and XBM). GIF is
not very suitable due to patent encumbrances, and Icon really needs to add PNG and JPG
support.
Window system native manipulation starts with off

screen invisible windows you can
draw on, and copy to visible windows from. A wi
ndow opened with "canvas=hidden" in
Icon can be used for this purpose; CopyArea(w1,w2,x,y,wd,ht,x2,y2) or
WAttrib("canvas=normal") are examples of ways to get hidden graphics onto the screen.
lecture #9 began here
lecture #10 (virtual lecture) began here
Lectures 9 and 10 were taught by Jafar. Any questions?
lecture #11 began here
Gamma Correction
The eye is more sensitive to ratios of intensity levels, rather than absolute values of
intensity. Intensity levels should be spaced logarithmically rather than linearly, to achieve
equal levels of brightness. Hill Section ??.? gives an overview of gamma c
orrection;
without gamma correction your range of color values will not be spread out smoothly.
Some hardware has gamma correction built

in, and some operating systems (e.g. MacOS)
implement gamma correction uniformly at the system level. For the rest, you
may have
reasonable default behavior but it may be to your advantage to allow the user to modify
the default gamma correction value for their nonstandard CRT's.
Review of Vector & Matrix Math
Please help Dr. J re

learn some of his vector and matrix math
from Hill Chapter 4 and
sites such as
Kwon's
GCSU
Wikipedia
lecture #12 began here
Once again with
Catmull Rom
Check out the
crsdemo
Ha
ve we Talked about Pixel() and WAttrib() enough yet
Pixel() really returns a pointer to (an array of) unsigned char RGB triplets, WAttrib()
returns a char * also.
2D Geometrical Transformations
Please absorb Chapter 4 in detail, the 2D part for starters
. Don't you just love those
homogeneous coordinates! Dr. J's thoughts: you can do 2D scaling and rotation with 2x2
matrices, that is as simple as anything here is going to get. To toss translation into the
mix, you resort to those pesky homogeneous coordin
ates, i.e. 3x3 matrices and a clever
trick to allow 2D translation, scaling, and rotation all at once. Although these are 3x3,
their bottom row tends to be dull; I am not sure how many libraries exploit that.
For 3D we end up with 4x4 matrices, employing
the same tricks. For exams (midterm,
final, etc.) you will have to know your matrix transforms down cold! Should we do a
pencil and paper homework?
Remember: matrices compose nicely, but not all transforms are commutative. Plan ahead
for the old translate

to

origin, rotate, and translate

back trick.
Cornellian lecturenotes
Wikipedia
lecture #13
began here
Read Chapter 5
We still need to hit one topic in Chapter 4, but we are obviously in Chapter 5 today.
Matrix Representations of 3D Coordinate Transformations
3D transformations can be represented by 4x4 matrices using homogeneous coordinates
(x, y, z, W), or (x/W, y/W, z/W, 1) for W != 0.
Translation is extended from the 2D case:
T(dx,dy,dz) =
1
0
0
dx
0
1
0
dy
0
0
1
dz
0
0
0
1
Scaling is similarly extended:
S(dx,dy,dz) =
Sx
0
0
0
0
Sy
0
0
0
0
Sz
0
0
0
0
1
Rot at i on i s a l i t t l e more i nt erest i ng; t he rot at i on around t he ori gi n t hat we di d before now
becomes rot at i on around t he z

axi s wi t h t he fol l owi ng mat ri x:
Rz(θ) =
cos θ
=

sin θ
=
0
=
0
=
=
sin θ
=
cos θ
=
0
=
0
=
=
0
=
0
=
1
=
0
=
=
0
=
0
=
0
=
1
=
But t here are t wo more axes one mi ght want t o rot at e about:
Rx(θ) =
1
0
0
0
0
cos θ
=

sin θ
=
0
=
=
0
=
sin θ
=
cos θ
=
0
=
=
0
=
0
=
0
=
1
=
Ry(θ) =
cos θ
=
0
=
sin θ
=
0
=
=
0
=
1
=
0
=
0
=
=

sin θ
=
0
=
cos θ
=
0
=
=
0
=
0
=
0
=
1
=
Composition of 3D Transformations (Hill 5.2.5 )
As was the case for 2D, 3D translation, scaling, and rotation can be composed as much as
you want, and reduced via matrix multiplication to a single matrix to apply to all points
that need transforming. Typi
cal will be a translation to the origin, a scaling, and as many
rotations as are needed to orient the object in the right direction.
Change in Coordinate Systems
One way to view transformations is as a "coordinate system conversion" similar to
converting
temperature from Fahrenheit to Celsius. To render a scene in world
coordinates you are changing world coordinates into "window" (logical) coordinates, and
then changing "window" coordinates into "viewport" (physical) coordinates. This same
approach, and t
he application of transformations to accomplish conversions, may be
extended into the application domain, allowing objects to express their coordinate
systems relative to whatever near neighbors are most logical, instead of expressing them
as absolute posi
tions relative to some world origin which is arbitrary.
All About Colors
This talk is based on material in [Foley/VanDam/Feiner/Hughes Chapter 13] and GB
Chapter 7. Hill Chapter 11 talks about color in some detail.
We have some basic introduction to col
or earlier, namely the RGB color coordinate
system commonly used on computer monitors. Computer hardware commonly uses 24

bits to express color information for each pixel, while software may use another
coordinate system, such as X11's 48

bit system.
It m
ay surprise you to hear that RGB color coordinates are a relatively new invention,
constructed solely as a by

product of the hardware used in color monitors. RGB is not
used in traditional color disciplines such as photography or print shops. Interestingly
,
RGB is not even capable of expressing many colors that humans recognize. We will see
aspects of that and other issues in this talk.
Intensity
Color is the perception of light, and the first coordinate that applies to all perception of
light is how much
light is seen: this is called intensity, luminance, lightness or brightness.
Intensity may range from 0 (black) through the grays up to infinity (white). Intensity
beyond a certain amount will cause blindness, so almost any upper bound of intensity that
i
s whiter than everything else might be considered white for a given view. Aside from
common binary systems (light vs. no light, a.k.a. monochrome), the question is: how
many different levels of brightness does the hardware support, and how many levels of
i
ntensity would be required for us to see it as a smooth continuum. One answer, from
Figures 13.1

13.6, is that 32 levels might be sufficient for practical purposes; Table 13.1
suggests that in real life the upper bound for most media are in the 400

500 int
ensities
range. If resolution is lower, more intensities may help, while higher resolution can
compensate somewhat for having fewer intensities. You can trade resolution for more
intensities using so

called half

toning and dithering techniques, especially
if you have
more resolution than you need, or are willing to stand viewers far away from what they
are looking at.
The truth is that humans vary a fair amount in their perception, ranging from those who
are blind to those who can see details far more prec
isely than average. As was mentioned
in the discussion of gamma correction, human perception of brightness is on a log scale;
we perceive ratios of intensity, not absolute values. Having appropriate gamma correction
might affect how many intensities are ne
eded in a given application.
Hue
For those viewers who are not color blind, the thing that determines perception of color is
the "dominant wavelength", ranging through the visible spectrum from the (ultra)violet
through the (infra)red. The term "hue" is
commonly used for this aspect of color. The
main thing for me to mention regarding hue is that although the spectrum occupies a
linear progression through wavelengths, humans do not perceive this linearity, if they
recognize a progression from violet to re
d it is learned artificially.
Saturation
It is quite rare for you to perceive "pure" light, almost all colors you see are mixtures.
Mixtures of specific colors may be averaged out to an intermediate color as a "dominant
wavelength", but for any color you
see we can generically ask how much white random
light is mixed in with whatever dominant colors are to be seen. If the answer is: 0% white
light, we would say the color is 100% saturated. If the answer is: 100% white light, we
have fully unsaturated ligh
t which will be seen as a gray (or white, or black) based on
intensity. Saturated colors are very vivid, while the more unsaturated would be
considered "washed out" or "pale".
Additive and Subtractive Color Models
The traditional coordinates for colors in some disciplines are HSV for Hue, Saturation,
and Value (=Intensity). But the "Hue" coordinate is not very easy to work, so other color
models are common. For hardware where colors are formed by adding light togeth
er, the
dominant coordinates RGB, and the "color cube" makes sense.
For colors that are formed by subtracting (filtering) out of white light from what will be
reflected, the colors cyan, magenta, and yellow are subtractive primary colors; they are
complem
ents of red (cyan="no red"), green (magenta="no green"), and blue (yellow="no
blue"). CMY coordinates are commonly used on color printers. Adding a fourth color
(pure black) typically improves the quality and reduces ink costs by giving blacker blacks
than
the dull gray you get from using all three CMY inks to produce "black" by regular
subtraction.
There are other significant color models in wide use; for example color TV signals use a
model (YIQ) that emphasizes intensity but also tacks on hue and satura
tion using a lower
amount of bandwidth. YIQ uses twice as much bandwidth for intensity as for all other
color information, and is mapped onto both monochrome and color (RGB) TV sets.
lecture #14 began here.
UIGUI Mouse x,y
We can't say &x and &y in C, so
we say Mx and My.
Quick thought on event

driven coding in UIGUI
You must call a GUI

event reading function constantly, or suffer dire consequences.
During any longer computations, call pollevent().
Clipping
Note: this lecture is a bit out of order, it should have been done earlier. In UIGUI,
Clip(x,y,wd,ht) clips all output to a specified rectangle in the window.
Hill Sections 3.3 and 4.7 talk about how clipping can be implemented. Let's see how
much we can
get from principles. The first clipping algorithm to do is: clipping lines.
Suppose we have to implement a function
DrawClipped(x1,y1,x2,y2,x,y,wd,ht)
that draws a line from x1,y1 to x2,y2, except skipping anything that is outside of
(x,y,wd,ht). How can
we write that procedure? Let's first dismiss some trivial cases. Can
we tell whether any clipping is needed at all? Yes, it is easy:
procedure DrawClipped(x1,y1,x2,y2,x,y,wd,ht)
if x <= x1 <= x+wd & x <= x2 <= x+wd &
y <= y1 <= y+ht & y <= y2 <=
y+ht then {
# draw the whole line, no clipping needed
return DrawLine(x1,y1,x2,y2)
}
# else some clipping is actually needed.
end
Now, how can we tell what clipping is needed? Here is Totally Easy Clipping:
if x1 < x & x2 <
x then return
if x1 > x+wd & x2 > x+wd then return
if y1 < y & y2 < y then return
if y1 > y+ht & y2 > y+ht then return
This narrows down the problem to: either one end needs to be clipped and the other
doesn't, or both ends need to be clipped. If
both ends need to be clipped, there are still
cases where the entire line is outside our drawing rectangle and needs to be clipped.
All of this up to now is totally obvious common sense, but it is also the start of the Cohen
Sutherland clipping algorithm
. It must be that what you do next is what makes Cohen
Sutherland interesting. Let's pretend that Dr. J hasn't read or doesn't remember Cohen
Sutherland even a bit. What options do I have?
Look for intersections between the (x1,y1) and the four line segme
nts of the
rectangle.
Divide line segment in half and apply clipping recursively
... other ideas?
Suppose we do the recursive thing:
procedure DrawClipped(x1,y1,x2,y2,x,y,wd,ht)
if x <= x1 <= x+wd & x <= x2 <= x+wd &
y <= y1 <= y+ht & y <= y2
<= y+ht then {
# draw the whole line, no clipping needed
return DrawLine(x1,y1,x2,y2)
}
# else some clipping is actually needed.
if x1 < x & x2 < x then return
if x1 > x+wd & x2 > x+wd then return
if y1 < y & y2 <
y then return
if y1 > y+ht & y2 > y+ht then return
# else: divide and conquer
DrawClipped(x1,y1,(x1+x2)/2,(y1+y2)/2,x,y,wd,ht)
DrawClipped(x2,y2,(x1+x2)/2,(y1+y2)/2,x,y,wd,ht)
end
Does it work? If you try this out, you will most likely get a
segmentation fault, because
we haven't adequately handled the base case (when is DrawClipped handling segments so
small that no recursion makes sense?). Cohen

Sutherland differs in that it isn't dividing in
half, but rather, looks to chop at the actual int
ersection points.
procedure clipSegment(x1,y1, x2,y2, x,y,wd,ht)
repeat {
p1_inside := (x <= x1 <= x+wd & y <= y1 <= y+ht)  &null
p2_inside := (x <= x2 <= x+wd & y <= y2 <= y+ht)  &null
if
\
p1_inside &
\
p2_inside then {
# draw the whole l
ine, no clipping needed
return DrawLine(x1,y1,x2,y2)
}
# else some clipping is actually needed.
if x1 < x & x2 < x then return
if x1 > x+wd & x2 > x+wd then return
if y1 < y & y2 < y then return
if y1 > y+ht & y2 >
y+ht then return
if /p1_inside then { # p1 not inside, fix p1
if x1 < x then { # p1 to the left, chop left edge
}
else if x1 > x+wd then { # p1 to the right, chop right edge
}
else if y1 < y then { # p1 above,
chop against top edge
}
else if y1 > y+ht then { # p1 below, chop against bottom edge
}
}
else { # p2 not inside, fix p2
}
}
end
In

class exercise: how to do the chopping?
If x1 < x, compute new x1,y1 inte
rsecting with x: slope m of segment is (y2

y1) / (x2

x1). New intersection point is (x, y1 + (x

x1) * m)
Check out my
Cohen

Sutherland
demo.
There is a lot more to say about clip
ping, especially in 3D. We will revisit this topic as
time allows.
Introducing OpenGL
OpenGL is public, standard C language 3D graphics API based on an earlier, proprietary
standard developed by SGI. OpenGL is a state machine of sorts, that takes in requests for
graphics primitives, processes them through a rendering pipeline, and produces
2D pixel
images as output.
OpenGL actually consists of two libraries (GL and GLU), and to use it you must either
write window system code yourself (X11 or Win32) or use a third party library such as
"glut" to handle window creation and user input.
In ord
er to compile OpenGL programs, you may have to install whatever packages are
needed for GL and GLU libraries and headers (libGL and libGLU in .so or .a forms, and
various <GL/*.h> files under some header directory such as /usr/include,
/usr/X11R6/include,
/usr/openwin/include, or whatever. You generally have to learn your
compiler's options for specifying where to look for these libraries (

L and

I).
In addition to these headers and libraries for linking, which you normally specify in a
makefile, you may
need a LD_LIBRARY_PATH environment variable in order for the
system to find one or more of your shared libraries at program load time in order to run it.
Note that you may already have an LD_LIBRARY_PATH, and you should just add your
OpenGL (e.g. Mesa) dir
ectory to your existing path if you have one.
Compared with the earlier Icon/UIGUI 2D interface we have seen, OpenGL has many
more features, and more complexity, for 3D programming. The glut library which
interfaces OpenGL to the host window system claims
to be simple and easy to use, but is
limited and restrictive in its capabilities. Last time I taught this course, students
complained bitterly about glut. Your options in this course are basically: glut or UIGUI
3D.
Callbacks
Most graphics API's are "ev
ent

driven", meaning that an application is written as a set of
functions which are called by the window system in response to user input or when other
services are required. glut follows this model in a strong sense; callback functions are
"registered" wi
th glut, after which glut's main loop owns the control flow of your
program. Under glut(), you do not draw your graphics output in your main() procedure,
you draw it in a "display callback" function that is invoked whenever the window needs
to be redrawn.
UIGUI does this sort of stuff for you under the hood; if you use it, your graphics
primitives get saved in a data structure, and the UIGUI library registers its display
callback function to walk and redraw that data structure. But for everything to work,
you
have to read a GUI input function or call pollevent() more or less constantly.
Composing graphics primitives
OpenGL graphics primitives are generally composed using a series of functions
expressed by the pattern:
glBegin glVertex+ glEnd
The glBegin(
type_of_object) function specifies what primitive is being depicted, and the
glVertex family of functions allow any number of (x,y,z) coordinates in a variety of types
and dimensions. This lecture is presenting selected material from the OpenGL Primer,
cha
pter 2.
lecture #15 began here
By the way... wormulon/eternium do not have glut?
At least, I do not see them, does anybody else? If not, this is part of what I was telling
you about. Ouch! I guess I had better ask Larry to put them up...
Looking at the
HW#3 "Solutions"
You are graded relative to your peers, but HW#3 results are pretty brutal so far. Let's take
a look.
A Bevy of OpenGL and GLUT Functions
In addition to the core graphics functions, there are a lot of helper functions in the
OpenGL API; we will present a few more details of these helper functions that you may
find useful.
int glutCreateWindow(char *title)
Regarding window creation with glu
tCreateWindow(), I need to emphasize the point that
you can create multiple windows, each call will return a separate integer "identifier". The
example
simple.c
is slightly misleading since it uses old

style K&R C, implying
glutCreateWindow() returns void.
Since the OpenGL functions don't take a window
argument, you can expect to find another helper function down the road which sets which
window subsequent calls are directed to, stored in some hidden global variable.
void glutDisplayFunc(void (*func)(void)
)
As the book emphasizes, your callback takes no parameters, so expect to use a lot of
global variables in your opengl programs.
glVertex*
There are 3 X 4 X 2 = 24 versions of this function! It may be moderately inefficient to
call this function 100 tim
es in order to specify a single, 100

vertex polygon. You should
be looking for whether there is any mechanism to streamline this.
glFlush()
Most graphics systems buffer output even heavily, just as standard file I/O systems do. In
particular X11 buffers
output to reduce the number of network packets it uses.
glutInitWindowPosition(x,y), glutInitWindowSize(width, height)
Most applications will use these typical convenience functions. They store values in
global variables for later use. Call them before g
lutCreateWindow().
Color in OpenGL
OpenGL supports "RGB" (true color) and "color index" (color map) modes; writing an
application that will run on either is a bit awkward, and color index display hardware is
vanishing, so it is reasonable to consider only the RGB color mode. Colors are spec
ified
in real numbers between 0.0 and 1.0. Some displays (notably, current generation Macs)
support degrees of transparency for superimposing multiple images on screen, and use a
fourth color component (alpha) where 1.0 means the color is solid and 0.0 mea
ns the
color is invisible.
Consider now the task of setting the foreground color with which objects are drawn.
Although colors are real numbers between 0 and 1, you can use almost any numeric type
to supply RGB values; integer values are converted into fr
actions of the maximum value,
so for example many programmers who are used to working with 8 bits each of R, G, and
B, can call a function that takes values like (255, 127, 0) and internally this is converted
to the equivalent (1.0, 0.5, 0).
So, like the
glVertex family, there are many (28) functions in the families that set the
foreground color, glColor*. An example call would be: glColor3f(1.0, 0.5, 0.0).
Apparently they didn't bother to make 28 functions for setting the background color with
glClearColo
r(), because that operation is far less common.
This discussion of color is well and good, but tragically it all becomes meaningless as
you transition from "toy" programs to more realistic ones, because once you introduce
lighting into your application, g
lColor*() has no effect! When lighting is introduced, the
color of objects becomes a function of the color of light and the reflective properties of
the objects, specified by "materials". We will see lighting in detail a little later.
Cameras, take one
O
penGL allows you good control over what "window on the world coordinates" will be
visible in your scene. In the general case, you will be able to specify viewing from an
arbitrary (x,y,z) location, in an arbitary (x,y,z) direction, as well as how wide and
high
your field of view is. Section 2.8 mentions the 2D special case of this,
gluOrtho2D(x1,x2,y1,y2); if you ever use it, beware the surprising parameter order, and
note that OpenGL's world coordinates are based on classic cartesian coordinates, not the
t
ypical physical coordinates in which y grows going down.
lecture #16 began here
OpenGL Transformation Matrices
The translation, scaling, and rotation used in converting objects' world coordinates to
their on

screen pixel locations is done automatically b
y OpenGL. Two transformation
matrices are maintained by OpenGL (in global variables) and combined to perform
rendering: the model

view matrix and the projection matrix. The same set of functions
are used for manipulating both matrices, so you start by spec
ifying which matrix you are
working on, by calling glMatrixMode(GL_PROJECTION) (or GL_MODELVIEW).
When you use gluOrtho2D() you are manipulating the projection matrix. Functions like
gluOtho2D modify (i.e. to a matrix multiply into) whatever is in the mat
rix already, and if
you want to start from a known position, you need to reset the matrix to the identity
matrix with glLoadIdentity(), so the complete code to specify a 2D window on the world
in OpenGL looks like
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(x1,x2,y1,y2);
Later on, we will see that you frequently compose these matrices, especially the model
view matrix, while drawing a complex scene. From one transformation corresponding to
a given "parent" coordinate system, if yo
u have several "child" coordinate systems whose
coordinates are relative to the parent, each child will need to save the parent
transformation, make changes and then draw their objects, and then restore the parent.
When object hierarchies are multiple leve
ls deep, a special type of stack is natural.
OpenGL provides a built

in stack mechanism, in which the "push" operation copies the
top stack element, to which the child may then apply transformations for their coordinate
system relative to the parent coordi
nates. glPushMatrix() and glPopMatrix() are used in
this way, and they operate on whichever current matrix you have specified using
glMatrixMode().
More OpenGL Graphic Primitives
Points
GL_POINTS are not usually very interesting in 3D graphics, but open
gl supports them
and even has a "point size" attribute (set by function glPointSize()) that says how large
(in pixels) plotted points should appear. Are points round, or square? Are they really
spheres in a 3D scene? The fact that their "size" is given in
pixels is at odds with the
world coordinates used to specify position, so you can be suspicious that points are a
"special case" hack in OpenGL.
Lines
There are three opengl primitives for depicting lines, similar to the two primitives used in
Icon's 2D
graphics. GL_LINES draws disconnected segments between pairs of points,
similar to Icon's DrawSegment(), while GL_LINE_STRIP draws lines between every
adjacent pair of points, forming a single connected object. GL_LINE_LOOP also the first
and last points s
pecified, for people too lazy to repeat the first point at the end. Lines
have a color, a thickness (glLineWidth()), and a so

called stipple pattern that let's you
specify dashes or dots in the lines you draw. But, you can only use these line styles if you
call glEnable(GL_LINE_STIPPLE).
Triangles, Triangle Strips, Triangle Fans
Triangles get a lot of attention in 3D graphics because they are the smallest number of
points necessary to specify a portion of a solid object's surface. 3D graphics hardware
sup
ports the rendering of triangles directly, while more complex surfaces are usually
composed of many many (possibly millions) triangles. In addition to GL_TRIANGLES,
are are cleverly optimized ways to specify sequences of physically connected triangles
with
out having to specify the vertices they have in common multiple times. The net effect
is only having to specify one new vertex per triangle. GL_TRIANGLE_STRIP connects
each vertex with the previous two, while GL_TRIANGLE_FAN connects each vertex
with the p
revious one, and the first vertex.
Quads and Quad Strips
Four vertex objects are also common, and may be rendered in hardware or broken up into
triangles for rendering. GL_QUAD_STRIP defines each new quad using the last two
vertices and two new vertices,
costing new vertices per quad. As an afterthought,
OpenGL also supports conventional 2D rectangles with the glRect* family.
Polygons
Comments 0
Log in to post a comment