The full set of slides

350
Computer Science 4751 Introduction to Computer Graphics Fall, 2014 1

Transcript of The full set of slides

Page 1: The full set of slides

Computer Science 4751

Introduction to ComputerGraphics

Fall, 2014

1

Page 2: The full set of slides

About this course

The major goal of this course is to understand the construction

of a 3-dimensional graphics system, including the underlying

basic algorithms and data structures. An additional goal is to

gain some experience with modern high-performance graphics

software.

Particular issues we will be concerned with are:

• Modeling — capturing and describing essential graphical

details

• Rendering — displaying the graphics on some output

• Realism — improving the quality of the image

This is not a course in the use of a particular graphics package

(although we will use OpenGL in the examples and assignments,

and you will be expected to achieve some proficiency in its use.)

Rather, we will attempt to understand how a graphics system

works, and the underlying algorithms, principles, and ideas.

2

Page 3: The full set of slides

What this course is not designed to do:

• To teach specific graphics packages — e.g. “business graph-

ics” or Autocad.

• To learn about graphical user interfaces (GUI’s) and their

toolkits (TK, Motif, . . . )

• To show you how to draw graphs of scientific or other data.

• To teach you to a programming language (or a new pro-

gramming paradigm). You should already be a competent

programmer.

• To use any particular programming environment, IDE, or

SDK.

• To make you a “graphics artist”.

Following this course, you may be have some insight into some of

those things, but we are more interested in how the underlying

graphics systems are designed.

3

Page 4: The full set of slides

Why OpenGL?

• It is available for virtually every OS

• It has bindings for many languages including C, Java, Python

• Many high-level graphics systems are based on it

• It is supported by most high performance hardware

• A smaller, simpler, (but still powerful) subset is optimized

for lower resource systems (OpenGL ES)

• It is supported for virtually all tablet systems and “smart

phones”

• It is free and open

In addition, knowledge of OpenGL is a valuable skill.

The aim of this course is not to learn OpenGL per se, but it is

a valuable side effect of the course.

Most of the examples will be implemented using the C language,

but it is straightforward to apply the same ideas to other lan-

guages, particularly Java and C++.

4

Page 5: The full set of slides

Models for graphics systems

There are many models for graphics “applications programmer

interfaces” (API’s). One model is the “press camera” model.

Note that the press camera was more flexible (literally) than

modern cameras — the lens could be adjusted horizontally and

vertically, and the backplane could be tilted.

5

Page 6: The full set of slides

In this model, the viewer, or “eye” is the lens of a camera,

the image is the camera backplane. The “scene” is whatever

appears in the field of view of the camera.

Viewport

"eye"

"Window"

The part of the scene in which the image to be displayed is usu-

ally called the “window” and is modeled in “world coordinates”

— usually in 3 dimensions.

The image is displayed in the two-dimensional “viewport” and

uses “device coordinates.”

6

Page 7: The full set of slides

Because the camera shows an inverted image, we often pretend

that it is a kind of mirror, projecting an erect image in front of

the “eye.”

Viewport

"Window"

As in the case of the press camera, the viewport may be tilted

with respect to the viewing axis.

Many 3-dimensional graphics systems use a similar kind of

model for displaying (or rendering) a scene.

An interesting feature of software graphics systems is that the

scene itself is also described using functions from that system.

7

Page 8: The full set of slides

Graphics systems like OpenGL provide functions for modeling a

scene, using some graphics primitives. These primitives often

have attributes which can also be altered independently.

The graphics primitives are usually simple shapes, and the at-

tributes are things like color or reflectivity.

A scene is described in world coordinates, and appropriate

models of lighting are associated with the scene.

Generally, then, a viewpoint, or eye position, is determined,

and the scene is rendered from that viewpoint by the graphics

system.

The image is then displayed on the viewport.

The order of events is:

Model ViewRender

program graphicslibrary

display

software hardware

8

Page 9: The full set of slides

Looking at an interactive graphics system as a whole:

Application

Program

Graphics

System

Application

Model

Display

InputAPI

OpenGL is one possible API which could be used to develop an

interactive graphics system.

Note that the interaction between the application program is bi-

directional. (The user can interact with the application program

through some graphical interface.)

The API (OpenGL) is used through a programming language.

OpenGL has language bindings for many languages, including

C, C++, Java, and Fortran. The C language bindings are used

in these notes.

OpenGL itself deals directly with the display. The input is

handled by another environment; in our case, the GLUT utility

functions.

These will be discussed later.

9

Page 10: The full set of slides

OpenGL

OpenGL is concerned with the drawing of an image (rendering)

into a framebuffer. It draws primitives, subject to a set of

modes.

Primitives are points, line segments, polygons, or pixel rectan-

gles.

Primitives are specified, modes are set, and other OpenGL op-

erations are accomplished by sending commands in the form of

procedure calls or functions.

There are OpenGL bindings to a number of languages; we will

look at the C language bindings.

Primitives are defined by a set of one or more vertices. Vertices

define points, which may be endpoints of edges, corners of a

polygon, or simply points, in two or more dimensions.

Attributes other than position (e.g., color, texture) may also be

be assigned to a vertex.

The model for interpreting OpenGL is the client-server model

(more about this later).

10

Page 11: The full set of slides

OpenGL command syntax – C bindings

OpenGL commands are functions or procedures, and follow a

simple naming convention in the C language. The following

shows the glVertex command:

void glVertexnt (args)

where

n is the number of dimensions (2, 3, or 4) and

t is the data type (integer, float, double, etc.).

For example, we could have

glVertex2f(x1, y1); /* 2 dimension, type float */

glVertex2i(i1, j1); /* 2 dimension, type int */

Another form of the glVertex command can use a pointer to

an array of vertices, as follows:

GLfloat vertex[3] /* an array of 3 elements */

glVertex3fv(vertex); /* 3 D, type pointer to float */

11

Page 12: The full set of slides

Sets of vertices are used to specify points, lines, polygons, and

surfaces using a glBegin - glEnd construct; e.g., for a line:

glBegin(GL_LINES);

glVertex2f(x1, y1);

glVertex2f(x2, y2);

glEnd();

We can define a set of points similarly:

glBegin(GL_POINTS);

glVertex2f(x1, y1);

glVertex2f(x2, y2);

glVertex2f(x3, y3);

glEnd();

In general, objects are defined with primitives of the form:

glBegin(object_type);

glVertex..(...);

.

.

.

glVertex..(...);

glEnd();

12

Page 13: The full set of slides

Other object types are polylines (lines made up of several,

connected, line segments):

GL_LINE_STRIP

The closed version is

GL_LINE_LOOP

For solid surfaces, the object types are polygons, triangles,

quadrilaterals, and special types called strips and fans.

GL_POLYGON

GL_TRIANGLES

GL_QUADS

GL_TRIANGLE_STRIP

GL_QUAD_STRIP

The tutorial shapes shows the use of the primitive drawing

functions.

Note that OpenGL only guarantees that convex polygons will

be rendered correctly.

13

Page 14: The full set of slides

V1 V3 V5 V7

GL_TRIANGLE_STRIP

V0 V2 V4 V6

V1 V3 V5 V7

GL_QUAD_STRIP

V0 V2 V4 V6

GL_TRIANGLES GL_QUADS

V1 V2

V3V0

GL_TRIANGLE_FAN

V0

V7

V6

V1 V2

V3

V5

V0

V7

V6

V1 V2

V3

V4

V5

V4

V4

V5

14

Page 15: The full set of slides

Other Attributes – color

Although color can be represented in a number of different ways,

we will initially use the RGB (red - green - blue) color model. In

fact, we will extend this to the RGBA model, where A (alpha)

is a measure of the transparency.

The quadruple

(1.0, 1.0, 1.0, 0.0)

would represent solid, opaque white — opaque because the

transparency is 0.0, and white because all of red, green, and

blue are saturated.

(1.0, 0.0, 0.0, 0.0)

would represent solid, opaque red — only the red component is

present.

what would the following represent?

(0.0, 0.0, 0.0, 0.0)

(0.0, 1.0, 0.0, 0.0)

(0.0, 0.0, 0.5, 0.0)

The tutorial shapes shows the use of both the primitive draw-

ing functions and color.

15

Page 16: The full set of slides

Projections

Often we are interested in a 2-dimensional display of things from

a 3-dimensional world, so we must project the 3-dimensional

objects on the plane.

One of the simplest projections is the orthographic projection.

Here, in effect, the viewer looks directly along one of the axes

of the coordinate system (usually the z-axis).

The field of view is also generally defined as a window, and it,

or part of it, is projected onto a 2-dimensional viewport.

Primitives intersecting the viewport are clipped, and primitives

lying entirely outside the viewport are not displayed at all.

OpenGL has primitives (functions) which have both a two-

dimensional and a three-dimensional form. The three-dimensional

form is most commonly used, and most of the examples we will

see use three dimensional primitives. The projection of these

primitives onto the viewing plane is something we will discuss

in detail later. For the present, the function

glOrtho(left, right, bottom, top, near, far)

defines a viewing volume bounded by (left,right) in the x-

dimension, (bottom,top) in the y-dimension, and (near,far)

in the z-dimension.

16

Page 17: The full set of slides

The display environment

Although OpenGL is often used for event driven, interactive

applications, OpenGL itself does not have functions for user in-

teraction or for interacting with the local environment (creating

a display window, for example).

These functions are performed by another library (in our case,

the GLUT library — actually, the freeglut library) which sets

up the display environment — window size, position, etc. and,

in fact, calls the OpenGL functions we will write.

We will discuss the GLUT library in more detail later. Initially,

we will use “boiler plate” functions into which we will embed

the code we wish to display.

The following simple OpenGL program draws a black rectangle

on a white background.

17

Page 18: The full set of slides

A simple program — hello.c

/* hello.c

*

* Copyright (c) 1993-1997, Silicon Graphics, Inc.

* ALL RIGHTS RESERVED

*

* This is a simple, introductory OpenGL program.

* It draws a black square on a white background.

*/

#include <GL/glut.h>

void init (void)

/* this function sets the initial state */

{

/* select clearing (background) color to white */

glClearColor (1.0, 1.0, 1.0, 0.0);

/* initialize viewing values */

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);

}

18

Page 19: The full set of slides

void display(void)

{

/* clear all pixels */

glClear (GL_COLOR_BUFFER_BIT);

/* draw black polygon (rectangle) with corners at

* (0.25, 0.25, 0.0) and (0.75, 0.75, 0.0)

*/

glColor3f (0.0, 0.0, 0.0);

glBegin(GL_POLYGON);

glVertex3f (0.25, 0.25, 0.0);

glVertex3f (0.75, 0.25, 0.0);

glVertex3f (0.75, 0.75, 0.0);

glVertex3f (0.25, 0.75, 0.0);

glEnd();

/* don’t wait!

* start processing buffered OpenGL routines

*/

glFlush ();

}

19

Page 20: The full set of slides

int main(int argc, char** argv)

/*

* Declare initial window size, position, and

* display mode (single buffer and RGBA).

* Open window with "hello" in its title bar.

* Call initialization routines.

* Register callback function to display graphics.

* Enter main loop and process events.

*/

{

glutInit(&argc, argv);

glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);

glutInitWindowSize (250, 250);

glutInitWindowPosition (100, 100);

glutCreateWindow ("hello");

init ();

glutDisplayFunc(display);

glutMainLoop();

return 0; // ANSI C requires main to return int.

}

20

Page 21: The full set of slides

Note that this does not look much like a conventional program

— there is really no “flow of control” to sequence events. This

is characteristic of event driven code — functions are written

which “handle” the various events.

In this simple example, there are no external events to handle;

there is no user interaction required, and any other types of

interaction (e.g. resizing a window) assume the system default

case.

Two functions were defined:

init() is simply a convenient place to put the initialization

information (the background color and projection)

display() is the function defining what is to be displayed in

the graphics window.

The functions glutInitWindowPosition(), glutInitWindowSize(),

and glutCreateWindow() set up a display window in a par-

ticular place, with a particular size and identifier on the screen.

glutDisplayFunc() calls the function which contains the OpenGL

drawing functions (display() in the present case).

glutMainLoop() enters the GLUT event processing loop (and

never returns). This causes the OpenGL commands to be exe-

cuted, as well as handling external events.

21

Page 22: The full set of slides

A more complex example — recursive refinement

Of course, we can use more complex programming structures

in our code. For example, the following code draws a simple

outline of a square, using 4 lines. The square is centered at the

origin, and has a “radius” of 1. (It is a crude approximation to

a circle.)

The program then recursively finds the center of each of the

lines, and normalizes the distance from the origin to 1, making

successively better approximations to a circle.

The program also parses a simple command line argument (an

integer) which it uses to set the level of recursion.

(The C language has I/O functions which are quite different

from JAVA or C++.)

1

22

Page 23: The full set of slides

/* recursive_circle.c

* This is a simple, introductory OpenGL program.

* It draws an approximation of a circle on a white

* background by recursively subdividing the sides of a

* square and renormalizing the distance from the origin.

*/

#include <stdio.h>

#include <stdlib.h>

#include <math.h>

#include <GL/glut.h>

int n_levels=0; // the number of levels of recursion

void init (void)

{

/* select clearing color as white */

glClearColor (1.0, 1.0, 1.0, 0.0);

/* initialize viewing values */

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);

}

23

Page 24: The full set of slides

// the recursive function

void drawside(float x0,float y0,float x1,float y1,int n)

{

float xm, ym, norm;

if (n <= 0) { // the stopping condition

glBegin(GL_LINES); // drawing only done here

glVertex3f(x0,y0,0.0);

glVertex3f(x1,y1,0.0);

glEnd();

}

else { // the recursion

n--; // decrement counter

xm = 0.5*(x0 + x1); // calculate midpoints

ym = 0.5*(y0 + y1);

norm = sqrt(xm*xm +ym*ym);

xm = xm/norm; // normalize line

ym = ym/norm;

drawside(x0,y0,xm,ym,n);

drawside(xm,ym,x1,y1,n);

}

}

24

Page 25: The full set of slides

void display(void) // the new display function

{

float x0, y0;

/* clear all pixels */

glClear (GL_COLOR_BUFFER_BIT);

/*

* set initial sides of square with unit "radius"

* centered at (0.0, 0.0, 0.0).

*/

glColor3f (1.0, 0.0, 0.0);

x0 = 1.0/sqrt(2.0);

y0 = x0;

drawside(x0,y0,-x0,y0,n_levels);

drawside(-x0,y0,-x0,-y0,n_levels);

drawside(-x0,-y0,x0,-y0,n_levels);

drawside(x0,-y0,x0,y0,n_levels);

glFlush ();

}

25

Page 26: The full set of slides

int main(int argc, char** argv)

/*

* Declare initial window size, position, and

* display mode (single buffer and RGBA).

* Open window with "circle" in its title bar.

* Call initialization routines.

* Register callback function to display graphics.

* Enter main loop and process events.

*/

{

if (argc == 2) sscanf(argv[1],"%i",&n_levels);

glutInit(&argc, argv);

glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);

glutInitWindowSize (400, 400);

glutInitWindowPosition (100, 100);

glutCreateWindow ("circle");

init ();

glutDisplayFunc(display);

glutMainLoop();

return 0; /* ANSI C requires main to return int. */

}

26

Page 27: The full set of slides

The function sscanf( ...) is one of the C input functions.

There are several, which do formatted input.

The simplest is the function scanf which takes input from the

standard input device (the keyboard). The function fscanf

takes input from a file which was previously opened.

The man pages give a brief description of these and other I/O

functions.

sscanf takes input from a character string and reads it accord-

ing to the specified format.

In this example, argv[1] is the character string corresponding

to the first command line argument for the program.

The constant character string "%i" is the format string, telling

the function that the string is an integer.

The third argument, &n levels, is a pointer to the integer

variable n levels which is the value of the integer which was

read.

The corresponding output functions are the printf family of

functions.

27

Page 28: The full set of slides

Simple animation

Animation is achieved by redrawing the screen repeatedly.

The GLUT interface to OpenGL has features to assist in ani-

mation. One is to double buffer the display memory. Another

is the provision of a function which causes the display to be

redrawn — usually after some change.

The following additions to the display and main functions

show a very simple animation of the previous example, in which

the color of the square is changed randomly every 0.2 second.

The random color change is accomplished with the following

change to the function display():

glColor3i (rand(), rand(), rand());

The double buffering and the re-running of the display()

function are effected by the following:

glutSwapBuffers();

glutPostRedisplay();

The C function

usleep(200000);

suspends execution of the process for 200000 microseconds.

28

Page 29: The full set of slides

void display(void)

{

/* clear all pixels */

glClear (GL_COLOR_BUFFER_BIT);

/* draw random color rectangle with corners at

* (0.25, 0.25, 0.0) and (0.75, 0.75, 0.0)

*/

glColor3i (rand(), rand(), rand());

glBegin(GL_POLYGON);

glVertex3f (0.25, 0.25, 0.0);

glVertex3f (0.75, 0.25, 0.0);

glVertex3f (0.75, 0.75, 0.0);

glVertex3f (0.25, 0.75, 0.0);

glEnd();

/* start processing buffered OpenGL routines */

glFlush ();

glutSwapBuffers();

usleep(200000);

glutPostRedisplay();

}

29

Page 30: The full set of slides

int main(int argc, char** argv)

/*

* Declare initial window size, position, and

* display mode (double buffer and RGBA).

* Open window with "new_hello" in its title bar.

* Call initialization routines.

* Register callback function to display graphics.

* Enter main loop and process events.

*/

{

glutInit(&argc, argv);

glutInitDisplayMode (GLUT DOUBLE | GLUT RGB);

glutInitWindowSize (250, 250);

glutInitWindowPosition (100, 100);

glutCreateWindow ("new_hello");

init ();

glutDisplayFunc(display);

glutMainLoop();

return 0; /* ANSI C requires main to return int. */

}

30

Page 31: The full set of slides

User interaction

Recalling that OpenGL itself does not have functions to allow

user interaction, then how can we provide the interactive com-

ponent to our graphics programs?

A number of API’s identify six types or classes of logical input

devices (or modes of interaction). They are as follows:

String The input is a character string. The input device could

be a keyboard.

Choice Allows selection from one of a fixed number of options,

say, from a menu or a set of buttons.

Pick An an identifier for an object is returned. The object is

usually pointed to by a mouse controlled pointer, or other

pointing device. (This is commonly used in WWW appli-

cations to select a function from a point in an image.)

Locator A locating device (usually a pointer) which provides

a position in “world coordinates.” The pointer is often

controlled by a mouse or touch sensor. (By “world coordi-

nates” we mean the coordinate space of the program with

which the interaction is taking place.)

31

Page 32: The full set of slides

Dial Allows “analog” input — input over a continuous range

of values. It is sometimes implemented as a graphical dial,

or as a slidebar.

Stroke Returns an array of locations, say, the path of a cursor

following a mouse movement, or finger on a touch screen.

Questions — extending technology

How could these modes of interaction map onto, say, voice in-

put? video input? touch sensors?

What modes would be applicable to each case?

Consider a telephone answering system. What modes are used

here?

What other kinds of input could be used?

Do you own any devices with other types of input? (Likely,

your cell phone has several.) Do you know how they work?

Would they map unto the modes described previously?

32

Page 33: The full set of slides

Input modes

There are a number of ways in which a program can interact

with input devices (either real or “virtual” devices):

• Request mode In request mode, the program re-

quests input from the device, and waits until the input is

available. The device is “triggered” by the request, and re-

turns a value after the device has made the “measurement.”

In simple systems (e.g., DOS), this is often accomplished

using a “busy wait” loop:

trigger device

loop: test for input

if (no input) then go to loop

continue with program

Note that only one device is tested for input, and the pro-

gram “wastes time” looping until the input is ready.

In a more sophisticated system (e.g., a multi-process sys-

tem like Windows or UNIX), the program would “sleep”

until the input was ready (this assumes the device has an

interrupt capability) and another process could run.

In either system, the program could not continue until the

input was provided.

33

Page 34: The full set of slides

• Sample mode In sample mode, the device is pre-

sumed to have a valid value whenever it is addressed. Here,

no wait loop is required before the value is received. Often,

though, it is the change in value that is of interest, and

again a wait loop would be required.

Again, information is obtained from the device only under

program control, and only when that particular input is

required by the program. This mode of input could be used

by, say, a clock program, to update the time displayed.

• Event mode Event mode is a more natural mode

for a system with several independent processes, but which

share one or more input devices (say, a windowing envi-

ronment, sharing a mouse and keyboard.) Event mode is

typical for a graphical user interface (GUI) and is supported

by the GLUT library.

In event mode, whenever an event for a device is triggered,

its value, together with an identifier, is placed in an event

queue or similar data structure. Exactly what can trigger

an event, and to which queue an event is posted, depends

on the system in question. It may also depend on what

other events have occurred.

34

Page 35: The full set of slides

Typical events could be pressing a keyboard key or a mouse

button, which might generate external interrupts. Other

events could be moving the cursor from one window to an-

other. This type of event would be determined by the oper-

ating system or window manager, and a kind of “software

interrupt” generated.

Typically, processes call functions which examine an event

queue for events of given types.

User interaction with OpenGL

The GLUT library has a number of functions relating to

the keyboard and mouse, and can be used to provide quite

sophisticated interaction with OpenGL programs.

GLUT also provides other interaction modes such as menus.

These will be discussed later.

35

Page 36: The full set of slides

GLUT mouse events

As an example of a GLUT function, we will look at a function for

a mouse event. A mouse event occurs when a mouse button is

depressed or released. The information returned includes which

button was pushed, the state of the button after the event (up

or down), and the position of the cursor in screen coordinates

of the window.

(Note that an event is generated for a button press only if the

button is pushed when the cursor is inside the window.)

The GLUT mouse function

glutMouseFunc(m_c_f)

requires as its argument a mouse callback function (here, m c f)

of the form

void m_c_f(int button, int state, int x, int y)

where button can have the values GLUT_LEFT_BUTTON,

GLUT_RIGHT_BUTTON, or GLUT_MIDDLE_BUTTON,

state can have values GLUT_UP, or GLUT_DOWN,

and x and y are the positions of the mouse pointer in window

coordinates.

This function defines the actions to be taken if the event occurs.

36

Page 37: The full set of slides

For example, we may wish to terminate the program on the

pressing of the right mouse button. The callback function would

be:

void m_c_f(int button, int state, int x, int y)

{

if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)

exit(0);

}

A more complex example might be if we wanted to draw a

square at the present mouse cursor position, following a left

button push, and exit on a right button push:

void m_c_f(int bt, int st, int x, int y)

{

if (bt == GLUT_LEFT_BUTTON && st == GLUT_DOWN)

drawSquare(x,y);

if (bt == GLUT_RIGHT_BUTTON && st == GLUT_DOWN)

exit(0);

}

37

Page 38: The full set of slides

The function drawSquare(int x, int y) could be:

void drawSquare(int x, int y)

{

y = wh - y /* wh == window height */

glColor3f(1.0, 0.0, 0.0)

glBegin(GL_POLYGON);

glVertex2i(x + size, y + size);

glVertex2i(x - size, y + size);

glVertex2i(x + size, y - size);

glVertex2i(x - size, y - size);

glEnd();

glFlush();

}

Here, wh is a global variable containing the window height, and

size is a global variable defining the size of the square.

Note that all the squares will be drawn red. (The sample pro-

gram shown later generates random colors.)

38

Page 39: The full set of slides

Mouse movement events

the function glutMotionFunc(m m c) sets the callback func-

tion for when mouse movement is detected in the current win-

dow, while a button is pressed.

It requires a callback function of the form

void m_m_c(int x, int y)

where x and y are the positions of the mouse pointer.

In the sample program following, the function drawSquare

shown previously is used as a callback for this function.

There is a complementary GLUT function

glutPassiveMotionFunc(m p c)

which sets the callback function for when mouse movement oc-

curs and no button is pressed.

39

Page 40: The full set of slides

Window events

On most systems, windows can be modified dynamically; e.g.,

the size or location can change. Such changes may give rise to

a window event, which may require service from the program.

For example, if an image is displayed and the window shrinks,

should the image shrink also, or should it be clipped?

Should the window style change if the window is resized? (By

adding scrollbars, for example.)

For window reshaping, The GLUT reshape function

glutReshapeFunc(reshape_callback)

is invoked. The callback function could resize the window,

change the projection, adjust the size of the viewport, mod-

ify the background color, etc.

Following is the code used in the square example:

40

Page 41: The full set of slides

/* reshaping routine called whenever the window

is resized */

void reshape_callback(GLsizei w, GLsizei h)

{

/* adjust clipping box */

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

glOrtho(0.0, (GLdouble)w, 0.0, (GLdouble)h,

-1.0, 1.0);

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

/* adjust viewport and clear */

glViewport(0,0,w,h);

glClearColor (0.0, 0.0, 0.0, 1.0);

glClear(GL_COLOR_BUFFER_BIT);

glFlush();

/* set global size for use by drawing routine */

ww = w; /* global variable for window width */

wh = h; /* global variable for window height */

}

41

Page 42: The full set of slides

Keyboard events

Keyboard events are generated when the mouse cursor is in the

window and a key is depressed. The ASCII code for the key,

and the mouse location are returned. The single GLUT function

for this is:

glutKeyboardFunc(keyboard_callback)

To quit on typing of the letter ‘’Q’ or ‘q’ for example, we could

write the following function:

void keyboard_callback( unsigned char key, int x, int y)

{

if (key == ’Q’ | key == ’q’) exit(0);

}

To quit when the ‘esc’ key is pressed:

void keyboard_callback(unsigned char key, int x, int y)

{

switch (key) {

case 27:

exit(0);

break;

}

}

42

Page 43: The full set of slides

An example program with interaction

/* square.c */

/* This program illustrates the use of the glut library

for interfacing with a Window System */

/* The program opens a window, clears it to black, then

draws a trail of random colored boxes at the location of

the mouse each time the left button is clicked and the

mouse is moved. The right button exits the program.

The program also reacts when the window is

resized by clearing the window to black */

#include <GL/glut.h>

/* globals */

GLsizei wh = 500, ww = 500; /* initial window size */

GLfloat size = 3.0; /* half side length of square */

43

Page 44: The full set of slides

void drawSquare(int x, int y)

{

y = wh - y;

glColor3ub( (char) random()%256, (char) random()%256,

(char) random()%256);

glBegin(GL_POLYGON);

glVertex2f(x+size, y+size);

glVertex2f(x-size, y+size);

glVertex2f(x-size, y-size);

glVertex2f(x+size, y-size);

glEnd();

glFlush();

}

/* mouse callback routine */

void mouse(int btn, int state, int x, int y)

{

if(btn==GLUT_RIGHT_BUTTON && state==GLUT_DOWN)

exit(0);

}

44

Page 45: The full set of slides

/* reshaping callback routine -- called whenever

window is resized */

void myReshape(GLsizei w, GLsizei h)

{

/* adjust clipping box */

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

glOrtho(0.0, (GLdouble)w, 0.0, (GLdouble)h,

-1.0, 1.0);

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

/* adjust viewport and clear */

glViewport(0,0,w,h);

glClearColor (0.0, 0.0, 0.0, 1.0);

glClear(GL_COLOR_BUFFER_BIT);

glFlush();

/* set global size for use by drawing routine */

ww = w; /* global variable for window width */

wh = h; /* global variable for window height */

}

45

Page 46: The full set of slides

void myinit(void)

{

glViewport(0,0,ww,wh);

/* Set 2D clipping window to match size of screen window

This choice avoids having to scale object coordinates

each time window is resized */

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

glOrtho(0.0, (GLdouble) ww, 0.0, (GLdouble) wh,

-1.0, 1.0);

/* set clear color to black and clear window */

glClearColor (0.0, 0.0, 0.0, 1.0);

glClear(GL_COLOR_BUFFER_BIT);

glFlush();

}

/* display callback -- required by GLUT 3.0 */

void display(void)

{}

46

Page 47: The full set of slides

/* The main program -- note that there is no "flow

of control" -- the program merely calls functions

to handle interactions */

int main(int argc, char** argv)

{

glutInit(&argc,argv);

glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);

glutCreateWindow("square");

myinit ();

glutReshapeFunc (myReshape);

glutMouseFunc (mouse);

glutMotionFunc(drawSquare);

glutDisplayFunc(display);

glutMainLoop();

return 0; /* ANSI C requires main to return int. */

}

/* Note that the function drawSquare has been used

as the callback for glutMotionFunc(). The drawing

is done only when glutMotionFunc() is active. */

47

Page 48: The full set of slides

Graphical output devices

Graphical display units are generally of two types: vector dis-

plays and raster displays.

Vector displays

Vector displays generally display lines, specified by their end-

points. The lines are generally smooth, and of a single color.

A common type of vector display is a pen plotter, although

many other vector displays are available.

While pen plotters are still in use for some functions, particu-

larly drafting and other line drawings, they have been generally

replaced by large-format color printers.

In fact, vector devices are becoming increasingly rare, due to

the difficulty of displaying solid surfaces with this technology.

48

Page 49: The full set of slides

Although vector devices are becoming rare, it is becoming more

common to store certain types of images as vectors. In fact,

many drawing programs (both 2D and 3D, for example, Au-

tocad and Inkscape) generate vector images, which are then

rasterized when displayed.

One key advantage of vector graphics is its scalability. A vector

description of an object can be rendered to virtually any size

without loss of detail.

What happens when a raster image is greatly enlarged?

Most internet browsers now support Scalable Vector Graphics

(svg) — a standard for vector graphics. The increasing resolu-

tion of displays and other output devices make the use of scale

independent graphics more desirable.

49

Page 50: The full set of slides

Raster displays

Raster displays typically have an array of addressable dots,

which can be individually set to a particular color or intensity.

The most common raster devices are displays (CRT and LCD)

and printers (inkjet, laser, dye-diffusion, dye sublimation).

We will examine briefly the technology for several of these de-

vices.

Cathode ray tube (CRT) technology

The standard computer monitor (or TV screen) is based on

the cathode ray tube (CRT). Essentially, a beam of electrons is

generated, and scanned back and forth over the inner surface

of a glass tube. The inner surface of the tube is coated with

a phosphor which emits light when electrons strike it. The

intensity of the emitted light from a point on the phosphor

depends on the number of electrons striking the phosphor.

The electron beam is scanned repeatedly over the phosphor

surface, with the intensity of the beam modulated to produce

the required dark and light spots. If the scanning rate is fast

enough, the screen produces a steady image.

(Television images are redrawn 30 times per second; high quality

monitors are refreshed 60 times per second, or more.)

50

Page 51: The full set of slides

Heatingfilament

(cathode)

Acceleratinganode

coils

Magneticdeflection

Focussingelectrode

Phosphorcoating

Electronbeam

Different phosphor materials emit different colors, so it is pos-

sible to construct a color display. Modern CRT devices use a

triad of colored phosphors — red, green, and blue — to produce

quite acceptable color output.

Usually, an electron gun is used for each of the primary colors,

and a perforated grid called a shadow mask is used to isolate

the electron beam to a single phosphor “dot”.

colortriad (phosphor)

shadow mask

"guns"Electron

51

Page 52: The full set of slides

The quality of a color CRT display depends on the number of

triads on the screen. There are a number of measures of this

“quality” — the number of lines (rows of dots) and the “dot

pitch” (distance between dots) being commonly quoted.

Presently, good monitors can exceed 2000 lines, and can have

more than 3800 triads per line. The dot pitch can be less than

0.25 mm.

(Regular television has 480 lines x 640 triads per line, and is

“interlaced” — every second line is redrawn each 1/60th of a

second, for a full refresh rate of 1/30th of a second.)

A “standard” 17 inch monitor (16 inches viewable) can have

specifications as follows:

0.25 mm dot pitch

refresh rate 48–120 Hz

1024 x 768 resolution at 65 Hz

1600 x 1200 resolution at 65 Hz

A high end monitor (24 inch) can have a dot pitch of 0.20 mm,

and a resolution of 2048 x 1536 at > 60Hz.

A high definition television (1080i) has a resolution of 1920 x

1080 at 60 frames per second (interlaced).

Recently, ultra high definition television is available with reso-

lution of 4096 x 2160 (or 3840 x 2160).

52

Page 53: The full set of slides

Liquid Crystal Display (LCD) technology

Liquid crystals are liquids with long organic molecules that can

polarize light. When an electric field is applied, the molecu-

lar alignment (and consequently the direction of polarization)

changes.

Basically, an LCD is a “sandwich” of liquid crystals between

two finely grooved plastic plates, where the grooves of one plate

are perpendicular to the grooves of the other.

Liquidcrystal

Polarizingfilters

lightBack-

In order to make a display, electrodes must be added. These

are on transparent layers, under the polarizing layers.

53

Page 54: The full set of slides

Different “shades of grey” can be obtained by varying the volt-

age across the electrodes. (Typically only 50–100 different shades

can be produced, per pixel.)

Liquid crystals can be colored to produce a color display, with

about 8–10 bit color resolution (256–1024 colors).

One problem with this type of liquid crystal is that it takes

about 1/4 of a second (250 ms.) to change state.

With this type display, rapid movement leaves “ghosts” for a

fraction of a second.

This problem can be eliminated by adding a transistor amplifier

to each pixel, so the voltage can be switched more rapidly.

This is a more expensive process, but produces pixels with a

response time about 10 times faster (25ms.) and with more

“shades of grey” — a contrast ratio of about 300:1.

This thin-film transistor technology (TFT) also allows smaller

and lighter displays.

Both types of LCD (TFT and DSTN) consume far less power

than a CRT (about 75% less), and are therefore almost exclu-

sively used in battery operated devices.

54

Page 55: The full set of slides

Hard-copy raster devices

The two major technologies for hard copy raster devices are

inkjet printers and laser printers.

Inkjet printers appear simple in operation; they merely force

drops of ink to spatter onto a sheet of paper, in fixed positions.

In reality, however, such printers can achieve resolutions of 1200-

2400 dots per inch (DPI) and considerable design effort has

gone into all aspects of these devices. (Special ink formulation,

droplet delivery, etc.)

Two methods are used to deliver drops; a piezo-electric pulse:

Piezodisc

Inkreservoir

cavity

Charge

Droplet

Nozzle

where changing the charge on the piezo disk causes it to expand

or contract and a droplet is forced from the outlet,

55

Page 56: The full set of slides

or a thermal technique in which some ink solvent is evaporated:

Heating resistor

Nozzle

Firing chamber

In either case, several hundred inkjets are contained in each

print head. All of these can be are fired independently.

Usually, drops are a single color, and color shades are obtained

by varying the number of color dots in a small area.

(In fact, this is also the case for color lithography using a print-

ing press.)

Some inkjet printers can mix different colors at a single point,

extending the color resolution.

Normally, four colors (red, green, blue, and black) are used, al-

though some printers may use additional colors for particularly

high color resolution.

56

Page 57: The full set of slides

Laser printers

Laser printers use a technology known as “xerography” in which

light is used to remove static electric charge from a drum, or

roller.

The roller is then placed in contact with a dry ink, some of

which bonds electrostatically to the drum. The ink powder is

then transferred to a sheet of paper, and the paper heated to

melt the ink and bond it to the paper.

For color printing, this is repeated four times — once for each

color — using four drums in series.

Laser Toner

Papertray

outputtray

Paper path

Heatingroller (fuser)

mirrorrotating

Drum

57

Page 58: The full set of slides

Other printing technologies

Instead of a laser, some printers use a liquid crystal shutter to

pattern the drum. Others use light-emitting diodes.

There are a number of other printer technologies. Two of special

note are a solid ink process in which dyes in a wax substrate

are deposited on a drum, and then cold-fused onto the paper

in one pass. This process is capable of mixing colors at a single

dot.

The most precise color technology is dye diffusion technology.

Here, the dye is heated until it becomes a vapor (sublimation)

and varying amounts of the dye are allowed to diffuse into points

on the paper. Again, four different colors are used, and each

dot can receive different amounts of each dye.

This process is slow and expensive, but produces very high qual-

ity color.

HeaterSheet of material

with dyes

PaperDye vapor

58

Page 59: The full set of slides

All the raster technologies have one thing in common — they

have a finite number of regularly spaced points which can be

set to some color.

A common abstraction for the programmer is the “frame buffer”

which is a data structure containing a number representing the

intensity of each color component at each of these points.

The most interactive component of a modern computer system

is the video output, and this usually has a dedicated controller,

as well as a frame buffer, separate from the processor and mem-

ory.

The most basic graphics card or graphics processor contains

a frame buffer and circuitry to convert the color values into the

voltages or currents used by the display. The graphics card also

generates the timing signals (horizontal refresh rate, pixel rate,

etc.) for the display.

Nowadays, graphics cards have powerful graphics processors

which handle many of the rendering functions and image trans-

formations directly in their circuitry. (We will describe many

of these functions later.)

59

Page 60: The full set of slides

The “graphics card”

A typical basic graphics card might look as follows:

Modern graphics cards contain much more memory than is re-

quired for the frame buffer.

Typically, some of this memory is used to store a description of

the scene, (information about primitives, vertices, colors, etc.)

which is interpreted by the graphics processor.

Other memory is used to store textures and other bitmaps.

60

Page 61: The full set of slides

The video processor

Modern video processors are very powerful computational de-

vices, rivaling that of the CPU. In fact, most have multiple,

pipelined processors. (Many graphics functions can readily be

parallelized.) The following shows the architecture of an early

Raedon processor by one of Canada’s largest graphics card de-

sign companies, ATI, now owned by AMD:

61

Page 62: The full set of slides

A recent moderate cost graphics card – note the

cooling for the graphics processors

Cards like this contain processors with far more raw computa-

tional power than in the CPU of the processor itself.

62

Page 63: The full set of slides

The previous graphics card, made by Nvidia, has a GPU (Graph-

ics Processing Unit) with much more raw computational power

than the CPU in the computer itself.

GPUs have become highly parallel multi-core systems enabling

efficient manipulation of large blocks of data.

Several generations of Nvidia processors have several sets of

banks of 192 core processors. These are effectively SIMD pro-

cessors, with each bank capable of running a different program.

The actual execution of the shader code, at the lowest level, is

run on these cores.

A high-end graphics processor may well have thousands (a mul-

tiple of 192) of those cores. For example, the GTX 780 Ti GPU

has 2880 of these units. (15 banks of 192 core processors.)

The next generation of Nvidia processors will have a different

microarchitecture, with 128 processors per block, but with in-

creased performance.

Their major competitor, the Radeon family from AMD, has a

similar kind of architecture, and also supports thousands of core

processors.

63

Page 64: The full set of slides

Can you see a 3-d figure here?

64

Page 65: The full set of slides

3-D displays

It is possible to generate a 3-dimensional (stereoscopic) image

by displaying two separate images, offset by a displacement cor-

responding to the eye displacement, in a way that the left eye

“sees” the left image, and the right eye “sees” the right image.

SCENE

The most common way this is done on a conventional display is

by using goggles with some kind of shutter system and rapidly

displaying on a screen a sequence of images synchronized with

the shutter.

Another type of system uses glasses which each reflect an image

from the surface of the glasses, or a small prism mounted in the

frame, directly into the left and right eyes, respectively.

There are also systems employing moving mirrors or projection

surfaces which allow a volumetric display — one which can

produce an image which can be viewed from different angles.

65

Page 66: The full set of slides

The company Dimension Technologies Inc. (DTI) has a tech-

nology called “parallax illumination” which can be applied to

LCD displays by adding an illumination plate behind the dis-

play which illuminates the even or odd column of pixels, de-

pending which image (left or right) is being displayed.

Pixels

DisplayLiquid Crystal

IlluminationPlateLight lines

For a certain range of viewing distances, and when both eyes

are directly in front of the display, the left eye will see one set

of columns, and the right eye will see the adjacent columns.

This parallax effect provides sufficient separation to produce a

stereoscopic effect without the use of goggles or glasses.

66

Page 67: The full set of slides

The effect disappears for off axis viewing, and for viewing at a

distance from the screen. It is possible to adjust the effect for

off axis viewing, by tracking the position of the eyes.

Viewing Zones

R R RL L LRL

Research into other related “auto-stereographic” viewing tech-

niques have produced results indicating that it may be possible

to produce similar stereographic images which can be viewed by

several people, provided that their eye motion can be tracked.

67

Page 68: The full set of slides

3-D “printing”

The technology for “numerically controlled machining” has been

available for a long time.

http://en.wikipedia.org/wiki/Numerical_control

Early machines were very expensive, and operated by removing

material from a workpiece, much as a machinist would do by

hand. More recently, machines which can form materials in dif-

ferent ways have become common, and at present many plastic

materials, and some metals, can be deposited in some way to

build structures by accreting material, rather than removing it.

Commercial systems use many different technologies; UV lasers

which polymerize a liquid plastic material, IR lasers which melt

small particles of material which fuse together, and extruders

which extrude soft or molten materials which then freeze in

place, building up a structure.

A common use is for the rapid prototyping of mechanical parts.

Presently, there are a number of low cost ”hobby” devices which

act as 3-D printers, typically heating a filament of plastic (usu-

ally ABS) to the point where it can fuse to other pieces of the

same plastic. Quite complex structures can be built up with

these devices.

68

Page 69: The full set of slides

Rep-Rap machines

One class of hobby level 3-D printer is the “Rep-Rap” machines

http://en.wikipedia.org/wiki/RepRap_Project

http://www.reprap.org/wiki/RepRap

These machine basically consist of smooth and threaded rods,

held together with plastic joints and other components printed

on a similar machine. The idea is that the machines are to

be self-replicating (at least, all but the metal and electronics

components).

The controller is typically a small, 8-bit microcontroller which

interprets commands which cause the print head to me moved.

Typically, movement is accomplished with “stepper motors.”

The drive mechanism is usually either a screw, or a gear and

ratchet (somewhat similar to a small bicycle chain.)

The extruder (which heats and extrudes the plastic material)

can be moved along three independent axes, although for the

most part, the material is deposited layer by layer.

69

Page 70: The full set of slides

A typical “home-made” Rep-Rap

70

Page 71: The full set of slides

One characteristic of most current graphics devices is that they

are raster devices — they approximate lines and surfaces with

discrete points. Even the 3-D printers usually deposit lines of

material, with small, fixed resolution.

Objects in the “real world”, on the other hand, have continuous

surfaces, and volumes.

Even OpenGL represents surfaces as polygons defined with ver-

tices, and the polygon edges are continuous straight lines joining

the vertices.

In order to represent these entities, we need to convert these

lines and surfaces to a set of points which best represents those

lines or surfaces at a particular resolution.

This process is called rasterization.

This is usually done at the time a scene is displayed on the

output device.

71

Page 72: The full set of slides

Rasterization

We will begin to examine how a graphics package like OpenGL

displays its primitive graphics elements.

The process by which primitives are converted to a two-dimensional

set of image points (pixels) is called rasterization. We can think

of the display as an array of rectangular pixels, each of which

can have a number of attributes (color, intensity, etc.)

012345

.

.

.

1 .2 3 4 5 ..0

The object is to determine which pixels form part of the object,

and then to assign the appropriate attributes to those pixels.

72

Page 73: The full set of slides

(Of course, in a real display, pixels may not be square, and there

may be attributes which cannot be displayed on a given device

— color on a monochrome device, for example.)

Note: In OpenGL, the center of a pixel is located halfway be-

tween the integers representing the array locations.

The preceding showed some of the difficulties with a raster im-

age — the “staircase” effect in the imaging of a straight line,

and the fact that primitives may have to be “clipped” by the

edge of a window.

There are ways of reducing the visual appearance of effects like

the staircase effect. This is called anti-aliasing, and usually

requires a wider linewidth. The general idea is to vary the

intensity of a pixel, so that it relates to the fraction of the line

passing through the pixel.

Given our simple set of primitives, how can they be rasterized?

Note that the “frame buffer” abstraction provides a complete

description of a raster image.

(Since the raster image is a finite array of “colored dots”, the

image is completely specified if we specify all the attributes of

each dot.)

73

Page 74: The full set of slides

In the following, we will assume that either the scenes we are

viewing are two-dimensional, or that they have already been

projected onto a two-dimensional surface.

We will also assume that they have been scaled to the view-

port, and that the individual pixel locations can be identified

by integers.

Further, two adjacent pixels are one unit apart.

x x

yy 1 unit

i i+1

j+1

j

The pixels form a unit grid, where each pixel has an integer

address in both the x and y dimensions.

Rendering is normally done in the viewport (the screen, in

screen coordinates), because integer arithmetic can be used for

most operations, and two-dimensional operations are simpler.

A 3-dimensional frame buffer might also require substantially

more memory.

74

Page 75: The full set of slides

Points

Points are represented by a square block of pixels. Most systems

require that a point be at least one pixel.

In many systems, points can be set to different sizes, and the

location of the pixels is obtained by either rounding or trunca-

tion.

Note that the two points here are the same size, but are rendered

differently. The difference depends on the actual location of the

pixel.

Anti-aliasing can be used here to make the two representations

more alike.

75

Page 76: The full set of slides

Lines

A simple algorithm for line rasterization, assuming the line has

been expressed in screen coordinates, is obvious from the equa-

tion for a straight line, yi = mxi + b

We simply evaluate the y value at each of the integer values of

x in the viewing area.

In fact, given an initial point, (x0, y0), the y value for the next

point could be calculated simply by adding the slope, m, to the

present y value (without evaluating the equation directly.)

Note that in the example shown, the line with the steeper slope

should have more points along the y-axis.

76

Page 77: The full set of slides

The algorithm could easily be modified to fix this by increment-

ing y by 1 and solving for x.

Because the value ofm is a real number, this algorithm requires

real arithmetic for the addition,

y = xmin * m + b;

for x= xmin, x <= xmax, x++ {

drawpixel(x, int(y + 0.5));

y = y + m;

}

This simple incremental algorithm is one of the simplest exam-

ples of a digital difference analyzer (DDA) algorithm. A DDA

was a mechanical calculator which used simple approximations

(finite differences)to the derivatives of a function to evaluate

the function itself. (For polynomials, a DDA which used high

enough order differences could give exact results.)

It used to be much slower to do real arithmetic than integer

arithmetic, so there was a strong incentive to find integer al-

gorithms for the primitive operations. (Now, many of those

functions are implemented in hardware, and integer arithmetic

uses fewer logic gates.)

77

Page 78: The full set of slides

The Bresenham algorithm

Bresenham’s algorithm, like the previous, is an incremental al-

gorithm, but uses only integer calculations.

We assume that the line segment goes through points (x0, y0)

and (x1, y1), and that the slope m satisfies 0 ≤ m ≤ 1.

In this case, if we have already drawn a pixel at one point, then

there are only two possible locations for the next pixel in the

line:

(x ,y )k k

M

A

Qb

a

B

Considering diagram A, we need only find which is larger, a or

b. In fact, if we take d = b − a as a decision variable, then

only the sign of d is important — if d is negative (i.e., b < a),

then the line is closer to the lower pixel.

It is possible to calculate d using only integer arithmetic.

The actual coordinate at point xk+1 = xk + 1 is

y = m(xk + 1) + B

78

Page 79: The full set of slides

so b = y − yk

= m(xk + 1) + B − yk

and a = (yk + 1)− y

= yk + 1−m(xk + 1)− B

subtracting,

b− a = 2m(xk + 1)− 2yk + 2B − 1

substituting m =y1 − y0x1 − x0

= ∆y/∆x

yields

b− a = 2(xk + 1)∆y/∆x− 2yk + 2B − 1

We can define a decision parameter

dk = ∆x(b− a)

= 2∆y · xk − 2∆x · yk + c

where c = 2∆y +∆x(2B − 1)

and is independent of pixel position (xk, yk).

If the pixel at yk is closer to the line path than that at yk + 1

then dk is negative. In that case, the lower pixel should be

plotted. Otherwise, the higher pixel should be plotted.

79

Page 80: The full set of slides

At step k + 1, the decision parameter is

dk+1 = 2∆y · xk+1 − 2∆x · yk+1 + c

Subtracting dk+1 from dk = 2∆y · xk − 2∆x · yk + c

dk+1 − dk = 2∆y(xk+1 − xk)− 2∆x(yk+1 − yk)

but xk+1 = xk + 1,

so dk+1 = dk + 2∆y − 2∆x(yk+1 − yk)

where (yk+1 − yk) is 0 or 1 depending only on the sign of dk.

d0 is evaluated by setting k = 0 as

d0 = ∆x(b− a) = 2(x0 + 1)∆y +∆x(−2y0 + 2B − 1)

= 2x0∆y + 2∆y − 2y0∆x + 2B∆x−∆x

= 2∆y −∆x + 2x0∆y − 2∆x(y0 −B)

but y0 −B = mx0 = x0∆y/∆x and therefore

d0 = 2∆y −∆x

From this, the dk can be evaluated recursively as:

dk+1 = dk + 2∆y − 2∆x[sign(dk)]

where sign(dk) is 0 if dk is negative, and 1 if dk is positive.

80

Page 81: The full set of slides

Bresenham’s line algorithm:

1. Input the endpoints (x0, y0) and (x1, y1)

2. Calculate constants ∆y,∆x, 2∆y, and 2∆y − 2∆x

and d0 = 2∆y −∆x

3. Plot the first point (x0, y0) in the frame buffer.

4. if dk < 0 then plot (xk+1, yk) and set dk+1 = dk + 2∆y

else plot (xk+1, yk+1) and set dk+1 = dk+2∆y − 2∆x

5. Increment k and go to 4 until k = ∆x

There are other integer algorithms which also can be used to

accomplish the same thing, but Bresenham’s line algorithm is

perhaps the best known.

For example, the book Computer Graphics: Principles and

Practice which was listed as a reference for this course describes

a similar algorithm, the midpoint algorithm, which is equivalent

to Bresenham’s algorithm for lines. It uses a similar idea, but

determines whether the line is above or below the midpoint

between the two pixels. The decision variable is the sign of

difference between the midpoint and the actual point.

81

Page 82: The full set of slides

A double step algorithm

Wu and Rokne (1987) developed an algorithm which looks ahead

at two pixels. Here, there are four possible cases.

case 3 case 4case 1 case 2

It is obvious that patterns 1 and 4 cannot occur on the same

line. Also, if the slope is > 1/2, case 1 cannot occur. Similarly,

if the slope is < 1/2 case 4 cannot occur.

So, a simple test of the slope narrows the choice to three cases.

Looking at the case where the slope is between 0 and 1/2, we

need to determine which of cases 1, 2, or 3 apply.

Setting the decision variable initially to d = 4∆y−∆x, then if

d < 0 then case 1 must be used; otherwise, test if d < 2∆y.

To increment d, the following rules apply:

dk+1 = dk + 4∆y if dk < 0 (case 1)

dk+1 = dk + 4∆y − 2∆x otherwise (cases 2 or 3)

82

Page 83: The full set of slides

Circles

Although OpenGL does not have a circle primitive, it is inter-

esting to look at how a circle could be rasterized.

A direct approach, solving the circle equation y = ±√R2 − x2

explicitly, is inefficient in a hardware implementation. (The

floating point square root function is more expensive than sim-

ple multiplication.)

The symmetry of a circle is helpful, for a circle centered at the

origin, because simple sign changes of the x and y values can

generate a full circle from only an octant (a 45◦ segment.)

(x,y)

(y,x)

(-x,-y) (x,-y)

(-x,y)

83

Page 84: The full set of slides

The midpoint algorithm for circles

It is possible to extend Bresenham’s algorithm to generate cir-

cles, but we will look at the (quite similar) midpoint algorithm.

Consider only one 45◦ octant of a circle, the segment from 90◦

to 45◦. The idea is to select which of two pixels is closer to

the circle by comparing the position of the line to the midpoint

between the two pixels.

Label the pixel directly to the right (East) of the current pixel

as E, and the other possibility — to the right and down — as

SE.

The function F (x, y) = x2+y2−R2 is 0 on the circle, negative

for points in the interior, and positive in the exterior.

MM

SEM

E

SE

E

We determine a decision variable d, which is the value of the

function at the midpoint, (xk + 1, yk − 1/2)

dk = F (xk + 1, yk − 1/2) = (xk + 1)2 + (yk − 1/2)2 −R2

84

Page 85: The full set of slides

If dk < 0, E is chosen, xk+1 = xk + 1, and yk+1 = yk, and

dk+1 = F (xk + 2, yk − 1/2) = (xk + 2)2 + (yk − 1/2)2 −R2

dk+1 − dk = 2xk + 3, so ∆E = 2xk + 3

otherwise, SE is chosen, xk+1 = xk +1, and yk+1 = yk − 1, and

dk+1 = F (xk + 2, yk − 3/2) = (xk + 2)2 + (yk − 3/2)2 −R2

dk+1 − dk = 2xk − 2yk + 5, so ∆SE = 2xk − 2yk + 5

Recall that, in the linear case, these were constants, but here

they are (simple) linear functions of xk and yk.

Now all that is necessary is to compute the initial position.

The starting pixel is at (0, R), the first midpoint at (1, R−1/2)

d0 = F (1, R− 1/2) = 1 + (R2 − R + 1/4)− R2 = 5/4−R

Although R is an integer, this expression has a fraction.

This can be eliminated by defining a new decision variable, h

as h = d− 1/4, and substituting h + 1/4 for d

The initial point, h0, is

h = 1−R

The comparison d < 0 becomes h < 1/4, but since h is an

integer initially, and is incremented by an integer (∆ E or ∆SE,

both of which are independent of d) we can use the comparison

h < 0.

85

Page 86: The full set of slides

The algorithm can be made more efficient by using the second

order difference to calculate the values of dk. (Since the circle is

a quadratic function, the second differences should be constant.)

To do this, we find the finite difference between the ∆’s for each

of the two possible choices (pixel at E or SE).

If the pixel is at E, then (xk+1, yk+1) = (xk + 1, yk),

∆E = 2xk + 3 and ∆SE = 2xk − 2yk + 5

∆2E = ∆Ek+1

−∆Ek= [2(xk + 1) + 3]− [2xk + 3] = 2

similarly,

∆2SE = ∆SEk+1

−∆SEk= [2(xk+1)−2yk+5]−[2xk−2yk+5] = 2

If the pixel is at SE, then (xk+1, yk+1) = (xk + 1, yk − 1),

∆E = 2xk + 3 and ∆SE = 2xk − 2yk + 5

∆2E = ∆Ek+1

−∆Ek= [2(xk + 1) + 3]− [2xk − 3] = 2

similarly,

∆2SE = ∆SEk+1

−∆SEk= [2(xk+1)−2(yk−1)+5]−[2xk−2yk+5] = 4

The ∆’s can be evaluated at each iteration k using the ∆2

terms corresponding to the present k (both ∆E and ∆SE must

be incremented with the appropriate ∆2 term at each step.)

86

Page 87: The full set of slides

Following is the midpoint algorithm for a circle, centered at the

origin, and starting from the point (0,R) and continuing for a

45 degree sector:

1. Input the radius, R

2. Set initial ∆E = 3 and ∆SE = 5− 2R, k = 0

3. Plot the first point (0, R) in the frame buffer.

4. if dk < 0 then set dk+1 = dk +∆E, ∆E = ∆E + 2,

∆SE = ∆SE + 2, x = x + 1

else set dk+1 = dk +∆SE, ∆E = ∆E + 2, ∆SE = ∆SE + 4,

x = x + 1, y = y − 1

5. plot (x, y) in the frame buffer

6. Increment k and go to 4 until y = x

The following code implements this version of the algorithm:

87

Page 88: The full set of slides

void raster_circle(int radius, int color)

int x, y, d, dE, dSE;

x = 0; /* initial point */

y = radius;

dE = 3; /* initial deltas */

dSE = 5 - radius * 2;

d = 1 - radius;

plotpoint(x, y, color);

while (y > x) { /* loop for 45 degree sector*/

if (d < 0) { /* chosen point is E */

d = d + dE;

dE = dE + 2;

dSE = dSE + 2;

x++;

} else { /* chosen point is SE */

d = d + dSE;

dE = dE + 2;

dSE = dSE + 4;

x++;

y--;

}

plotpoint(x, y, color);

} }

88

Page 89: The full set of slides

Rasterizing solids — polygons

Two-dimensional filled figures are one of the major advantages

of raster images.

The filling of images is usually broken into two parts — deter-

mining what pixels are interior to the clipped primitive, and

therefore are to be filled, and exactly how the primitive is filled

(e.g., shading, transparency, etc.)

We will look first at how to determine which pixels to fill.

Initially, we will consider an unclipped polygon, to be filled with

a solid color. We will do this by taking successive horizontal

scan lines that intersect the primitive, and filling in the spans

of pixels that are interior to the primitive. (this is similar to

the way pixels are displayed on a CRT.)

CurrentScan line

Span algorithms exploit spatial coherence — the property that

adjacent pixels do not change (unless at a polygon boundary.)

89

Page 90: The full set of slides

This type of spatial coherence is often called span coherence.

Some hardware graphics engines have a “block copy” function

which copies a block of pixels very rapidly.

We will first consider the filling of simple, convex polygons like

triangles. Here, a simple algorithm could be:

1. Draw the polygon boundary and label boundary points

2. On each scan line, color all points between the two bound-

ary points

interior pixel

exterior pixel

Note that there is a problem here; for a polygon, we want to

fill interior pixels only. When a line is drawn, some pixels will

fall on either side of the “true” line. Only those interior to the

polygon should be rendered in a solid figure.

Also, what happens to the fill for horizontal lines?

Recall that OpenGL only guarantees correct rendering of convex

polygons.

90

Page 91: The full set of slides

To extend this to non-convex polygons, we need to keep an

ordered list of intersections. A left-to-right traversal of these

could then be used to determine whether a pixel was interior or

exterior (a parity check on the number of intersections.)

Note again that a filled polygon and a line figure with the same

vertices may have different boundaries.

Exterior polygon boundary (line)Interior polygon pixels

Interior polygon boundary

A

B

E

C

D

Again, there are some unanswered questions:

91

Page 92: The full set of slides

1. What happens when two polygons meet at a pixel?

2. What happens when two polygons share a vertex?

3. How do we treat interior vertices of a non-convex figure

(like vertex B in the previous figure)?

4. What happens when vertices define a horizontal line?

For the first case, arbitrarily, the left and bottom edges (mini-

mum x and y values) are considered to be part of the polygon,

while the right and top edges are not. (This effectively treats

the scan lines in a polygon as left closed, right open intervals.)

Any other consistent choice would work as well.

For shared vertices, the ymin vertex of an edge is counted, but

not the ymax vertex.

This also works for the case of an interior vertex in a non-convex

figure. (Consider vertex B in the previous figure — it is counted

as the ymin of both AB and BC, so act as if it was two vertices.)

Horizontal edges are handled by not counting their vertices.

(They will be dealt with correctly as vertices of other edges.)

92

Page 93: The full set of slides

Example:

Shared vertex

4142434445464748495051

82 84 85 86 87 88 89 90 91 92

Shared edge

83

Note that the shared edge is the upper edge of the red (lower)

rectangle, and the lower edge of the green (upper) rectangle, so

it is colored green.

The lower shared vertex of the triangle is on its left edge, so

it is part of the triangle (blue), not part of the (red) rectangle,

where it is on the right edge.

The upper shared vertex of the triangle is the top, so it is part

of the upper rectangle (green).

93

Page 94: The full set of slides

Scan-line intersections with primitives

So far, we have not considered the problem of computing the

intersection of the scan line with the primitives — there are

many scan lines, so this computation should be done efficiently.

(Some extremely efficient algorithms are available for special

classes of figures; rectangles, for example.)

This is where edge coherence is useful — it is likely that most

edges intersecting scan line i also intersect line i + 1. If we

know the slope of the edge, we can use a method similar to the

line rasterizing algorithms to determine the intersections with

the next scan line. As with the rasterization algorithms, the

arithmetic can be done with integers, for a rational slope, m,

by noting that the pixel in the next scan line is at position

xi+1 = xi + 1/m = xi +xmax − xmin

ymax − ymin= xi +∆x/∆y

The following code snippet (from the text) applies this process

to the left edge of a polygon:

94

Page 95: The full set of slides

void LeftEdgeScan(int xmin,int ymin,int xmax,

int ymax,int color)

{

int x, y, dx, dy, increment;

x = xmin;

dx = xmax - xmin;

dy = ymax - ymin;

increment = dy;

for (y = ymin; y<ymax; y++){

plotpixel(x, y, color);

increment += dx;

if (increment > dy){

/* Overflow, so round up to next pixel

and decrement the increment */

x+= 1;

increment -= dy;

}

}

}

95

Page 96: The full set of slides

We can develop a scan-line algorithm which takes advantage

of this edge coherence. We need to keep track of the sets of

edges that have (or will have) intersections with the present

and upcoming scan lines.

We will create two tables holding information about polygon

edges.

First, we create a global edge table (ET) containing all edges

sorted by their smaller (lower) y coordinate. There may be

several edges with the same lower y coordinate, and those are

sorted by their x coordinates (a bucket sort, where each bucket

contains the values sorted by their x coordinate.)

The table index is ymin and the entries are the maximum y

values ymax for each edge, the minimum x value (xmin), and

the increment for the x value (1/m).

We create a second data table called the active edge table

(AET) to which we will add the edges which will intersect with

the scan line, and delete edges which will not.

96

Page 97: The full set of slides

For the previous figure, redrawn here, the following is the ET

with entries in the form (ymax, xmin, 1/m):

41 (47, 82, 0) → (47, 86, 0) → (47, 86, 1/2) → (47, 91,−1/3)

47 (51, 82, 0) → (51, 92, 0)

Shared vertex

4142434445464748495051

82 84 85 86 87 88 89 90 91 92

Shared edge

83

(The rectangle and triangle begin on scan line 41, and have 2

edges each. The upper rectangle begins on line 47, and has 2

edges. Horizontal edges are not included.)

97

Page 98: The full set of slides

Once the ET has been formed, the following processing steps

for the scan-line algorithm are completed:

1. Set y to the smallest y coordinate that has an entry in the

ET, that is, y for the first nonempty bucket.

2. Initialize the AET to be empty.

3. Repeat until the AET and ET are empty;

(a) Move from ET bucket y to the AET who edges whose

ymin = y (entering edges).

(b) Remove from the AET those entries for which y = ymax

(edges not involved in the next scan line), then sort the

AET on x (made easier because ET is presorted).

(c) Fill in desired pixel values on scan line y by using pairs

of x coordinates from the AET.

(d) Increment y by 1 (to the coordinate of the next scan

line).

(e) For each nonvertical edge remaining in the AET, update

x for the new y.

98

Page 99: The full set of slides

Flood filling

Another, relatively simple, technique for filling a polygonal area

is the flood fill or boundary fill algorithm. This method is often

available in “paint programs.”

A point interior to a polygon is chosen, and the entire polygon is

filled, until a boundary line is reached. (Usually, the boundary

is taken to be a pixel with a color other than the background

color.)

A simple algorithm examines the 4 pixels in the vicinity of the

chosen pixel, and if they are not boundary pixels, they are given

the same color as the chosen pixel. The process then continues.

This could be done, possibly more efficiently, for the 8 nearest

neighbours of a pixel, or for other patterns, as well.

99

Page 100: The full set of slides

The following code implements the algorithm for four neigh-

bours recursively:

void floodfill (int x, int y, int color, int boundary)

{

int current;

current = getpixel(x,y);

if ((current!=boundary) && (current!=color)) then

plotpixel(x, y, color);

floodfill (x + 1, y, color, boundary);

floodfill (x - 1, y, color, boundary);

floodfill (x, y + 1, color, boundary);

floodfill (x, y - 1, color, boundary);

}

}

For the 8 nearest neighbours of a pixel, the modification would

merely be to add function calls for arguments x + 1, y + 1.

etc.

100

Page 101: The full set of slides

Clipping

Since the viewing area, or viewport, may be smaller than the

scene to be displayed, primitives may have to be clipped at the

edges. We will look at the clipping of our primitives.

Points

For a rectangular clipping region with boundaries xmin, xmax,

ymin, and ymax, a point must simply lie within the boundary

region.

Both the following inequalities must hold:

xmin ≤ x ≤ xmax and ymin ≤ y ≤ ymax

A point can therefore be trivially rejected if any of those four

inequalities does not hold.

max

min min

max max

min

viewport

y > y

(x , y )

(x , y )

x < x

101

Page 102: The full set of slides

Lines

Lines can also be trivially accepted if both endpoints lie within

the clipping boundary. If both endpoints are outside the clip-

ping boundary, the line may still pass through the clipping re-

gion.

One approach is to look for an intersection with any of the clip

rectangle edges. If there is such an intersection, the line is partly

within the clipping region.

This requires solving two simultaneous equations for each edge,

and for each line. (For n lines and 4 edges, there are 4n sets of

equations.)

C

E

F

G

H

G’

I’

A H’C’

D

J’

J

B

I

The normal slope-intercept formulation for a line does not work

for vertical lines, and we deal with finite, not infinite, lines.

102

Page 103: The full set of slides

A parametric formulation can solve this problem: x = x0 +

t(x1 − x0) and y = y0 + t(y1 − y0)

This still requires solving two equations for each edge of the

clipping region.

A more efficient algorithm, the Cohen-Sutherland line clipping

algorithm, does simple region checks if a line cannot be triv-

ially accepted. For example, line segment AB could be triv-

ially rejected because both endpoints are < xmin. Line seg-

ment EF is trivially accepted because both endpoints satisfy

xmin ≤ x ≤ xmax and ymin ≤ y ≤ ymax.

C

E

F

G

H

G’

I’

A H’C’

D

J’

J

B

I

If a segment cannot be trivially accepted or rejected, it is divided

into two segments at a clip edge, so one segment can be trivially

rejected (e.g., CD into C’D). A line may require several “clips.”

103

Page 104: The full set of slides

The Cohen-Sutherland line clipping algorithm

This algorithm has a clever way to reject line segments.

To perform the trivial accept/reject tests, the edges of the clip

region are extended, defining nine separate regions. Each region

is labeled by a 4-bit binary number, the outcode, where each bit

(1 = true, 0 = false) corresponds to the following conditions:

bit 1 (MSB) y > ymax above top edge

bit 2 y < ymin below bottom edge

bit 3 x > xmax right of right edge

bit 4 (LSB) x < xmin left of left edge

1000 1010

01000101 0110

1001

00000001 0010

Each bit of the outcode is simply the sign bit of the differences

(ymax − y), (y − ymin), (xmax − x), (x− xmin), respectively.

Endpoints of a line are assigned their outcodes, and if the log-

ical AND of both outcodes is nonzero, the line can be trivially

rejected. The line is trivially accepted if the outcode is 0000.

104

Page 105: The full set of slides

If a line cannot be trivially accepted or rejected, it must be

subdivided into two segments so that one or both can be trivially

rejected. The line is cut using an edge that the line crosses.

The outcode has the useful property that bits set to 1 in the

outcode correspond to edges crossed.

If a line is not trivially rejected, this property can be used to

identify an endpoint which is outside the clipping region, and

an edge which the line crosses. The endpoint is replaced by the

point at which the line crosses the clipping line.

Of course, the order of clipping is fixed; for our example it cor-

responds to the order of the bits in the outcode — top, bottom,

right, left.

The intersection is readily calculated from the slope-point for-

mula:

y − y0 = m(x− x0), where m =y1 − y0x1 − x0

105

Page 106: The full set of slides

Example

1000 1010

01000101 0110

1001

00000001 0010

A

B’’B’

B D

C

Here, for line AB, A is inside the clip region (outcode 0000) and

B is outside, upper right (outcode 1010). Point B is chosen, and

clipped first against the upper boundary at B’. The segment AB’

cannot be trivially accepted or rejected (B’ is outside, right, with

outcode 0010) and must be clipped against the right boundary.

This leaves segment AB” which is trivially accepted (both A

and B” have outcodes 0000).

How many times will line CD be clipped before it is trivially

rejected?

No, two. Why?

106

Page 107: The full set of slides

The Cyrus-Beck algorithm for line clipping

Another line clipping algorithm uses parametric expressions for

lines. A line is represented as

P (t) = P0 + t(P1 − P0) 0 ≤ t ≤ 1

Given a clip region (say, the left edge of a rectangle) as shown,

with edge Ei, and outward normal Ni to the edge, and picking

an arbitrary point PEion the clipping edge, a point can be

determined to be inside or outside the boundary by looking at

the sign of the dot product of the normal Ni and the vector

between PEiand P (t), as follows:

t

t

t

t

t

t

✟✟✟✟✟✟✟✟✟✟✟✟✟

✡✡

✡✡

✡✡

✡✢

PPPPPPq

Ei

PEi

P0

P1

Ni

Ni.[P (t)− PEi] = 0

Ni.[P (t)− PEi] < 0

Ni.[P (t)− PEi] > 0

Clearly, at the clipping boundary,

Ni · [P (t)− PEi] = 0

so we can solve for t after substituting P (t) = P0 + t(P1 − P0)

Ni · [P0 + t(P1 − P0)− PEi] = 0

107

Page 108: The full set of slides

Ni · [P0 − PEi] +Ni · t[P1 − P0] = 0

Letting vector D = (P1 − P0)

t =−Ni · [P0 − PEi

]

Ni ·DThis is applicable if Ni ·D 6= 0.

Similarly, we can find values of t for each of the clipping bound-

aries. (The boundaries do not even need to be rectangular.)

We need to determine which line segments lie within the clip

boundary. Certainly, any values of t > 1 can be discarded,

because they are outside the line P0P1.

Next, we need to know whether the intersection is on the clip

boundary. We can characterize the intersections as potentially

entering (PE) or potentially leaving (PL) the clip region by

the angle between P0P1 and Ni. If the angle is greater than 90◦

then it is PE, otherwise it is PL.

The sign of Ni ·D gives us this:

Ni ·D < 0 ⇒ PE (> 90◦) Ni ·D > 0 ⇒ PL (< 90◦)

We now choose the PE point with the largest t value (if t < 0,

then t is set to 0), and the PL point with the smallest t value

(if t > 1, then t is set to 1).

If the t for PL is larger than t for PE, then the line is entirely

outside the clipping region.

108

Page 109: The full set of slides

P0

PL

PE

PL

P

PPL

PL

0P

1

1

P

PP

0

1

PLPE

PE

PL

PE

PLPE

P1

P0

Note that for the top line, PL < PE, so it is outside the clip

boundary.

The bottom line is also outside the clip boundary, but what

condition could be tested to determine this?

109

Page 110: The full set of slides

Clipping polygons — the Sutherland-Hodgman al-

gorithm

This algorithm clips a polygon against a line (or plane), for each

line (or plane) determining the clipping region.

The algorithm steps from vertex to vertex, adding 0, 1, or 2

vertices to the output list at each step.

Case 2 Case 3 Case 4Case 1

C

Doutsideinside

B

A

C

Doutsideinside

B

A

C

Doutsideinside

B

A

C

Doutsideinside

B

A

C’

D’

Assuming vertex A has already been processed,

Case 1 — vertex B is added to the output

Case 2 — vertex C’ is added to the output (edge BC is clipped)

Case 3 — no vertex added (segment CD clipped)

Case 4 — vertex D’ is added to the output (edge DA is clipped)

Typically, this is coded recursively — the recursive implemen-

tation lends itself to efficient hardware implementation.

The clip region can be any convex polygon.

110

Page 111: The full set of slides

Introduction to modeling

So far, after a brief introduction to OpenGL, we have looked

primarily at the “back end” of a graphics system — the display

devices, and the algorithms used to render the basic primitives

on the display.

Effectively, we have looked at constructing geometric primitives

on a 2-dimensional display.

We have taken as a starting point some data structure contain-

ing sets of vertices, and discussed how to draw the appropriate

primitives associated with those vertices — points, lines, and

convex polygonal surfaces.

We have also looked at how to make the algorithms which render

those primitives efficient, and at how to clip the primitives at

the boundaries of the viewport.

We have not concerned ourselves much with how those prim-

itives are put together to form more complex graphical struc-

tures.

In effect, we have confined ourselves to the 2-dimensional world

of the display, and we want now to look at the 3-dimensional

world in which we will describe the scene to be rendered.

111

Page 112: The full set of slides

Vectors and transformations

Often we want to apply certain transformations to graphical

objects; particularly common transformations are translation,

rotation, scaling, etc. in either 2 or 3 dimensions.

Translation

If we represent graphics primitives using vectors, translation is

easily accomplished by vector addition. The required displace-

ment vector is added to the vertex vectors of the primitives.

Example: Given a triangle with a set of vertex vectors

V = {(2,2), (4,6), (6,2)} and a displacement vector T = (1, 1)

the resultant vertex set for the triangle is V ′ = {(3,3), (5,7), (7,3) }

1 2 3 4 5 6 7 8

12345678

1 2 3 4 5 6 7 8

12345678

Before After

This could be written as V ′ = V + T

112

Page 113: The full set of slides

Scaling

The vectors can be uniformly scaled by simply multiplying each

vector by a scalar constant. Note that the full vector (from the

origin to the point) will be scaled, so the image will change in

both size and position.

1 2 3 4 5 6 7 8

1

2

3

4

5

6

7

8

1 2 3 4 5 6 7 8

1

2

3

4

5

6

7

8

AfterBefore

Scaling by 0.5

A differential scaling is also possible, where the x and y di-

mensions are treated differently. This can be accomplished by

multiplication by a diagonal matrix containing the scale factors.

x′

y′

=

sx 0

0 sy

·

x

y

This would often be written as

V ′ = S · V

where x and y are the components of V.

113

Page 114: The full set of slides

Note that vectors are written as column vectors; the earlier

notation for a vector with its components in parenthesis is also

common, and means the same thing.

The following is the figure seen earlier scaled by the matrix

S =

0.5 0

0 0.75

1 2 3 4 5 6 7 8

12345678

1 2 3 4 5 6 7 8

12345678

AfterBefore

Note that the size, position, and shape of the figure has changed.

114

Page 115: The full set of slides

Rotation

Consider two coordinate systems (x, y) and (x′, y′)

x

x cosy sin

y

x’

y’

θθ

θ

Here x′ = x cos θ − y sin θ

and y′ = x sin θ + y cos θ

In vector form, this is written as:

x′

y′

=

cos θ − sin θ

sin θ cos θ

·

x

y

or V ′ = R(θ) · V

Note that angles are measured in the counterclockwise direction.

The effect of multiple successive rotations can be derived from

the product of the corresponding rotation matrices.

115

Page 116: The full set of slides

The three types of transformations have the form:

Translation V ′ = V + T

Scaling V ′ = S · VRotation V ′ = R · VThe results of successive rotations and scalings can be obtained

by matrix multiplication, but translation is different.

Since we often want to apply the same set of transformations

to many primitives, it would be very useful if we could combine

all the transformations into a single matrix.

Homogeneous coordinates

In fact, using homogeneous coordinateswe can use matrix mul-

tiplication to implement all three of the basic transformations.

In homogeneous coordinates, a third coordinate is added to a

point; point (x, y) is represented as (x, y,W ).

Two homogeneous coordinates represent the same point if they

are multiples of each other. In fact, there are infinitely many

representations of the same point.

If we set W to be 1, (the point would be (x/W, y/W, 1)) then

we have homogenized the point.

116

Page 117: The full set of slides

Now, translation can be performed with matrix multiplication,

so translation by (dx, dy) would be represented as

x′

y′

1

=

1 0 dx

0 1 dy

0 0 1

·

x

y

1

Multiple translations are performed by matrix multiplication.

The transformations for scaling remain the same:

x′

y′

1

=

sx 0 0

0 sy 0

0 0 1

·

x

y

1

The transition matrix for rotation is also unchanged:

x′

y′

1

=

cos θ − sin θ 0

sin θ cos θ 0

0 0 1

·

x

y

1

Successive scalings and rotations can also be implemented with

matrix multiplication.

Remember, though, that if the transformations are mixed, the

order of application of the matrix operators is important. (Ma-

trix multiplication is not commutative — AB 6= BA, in general.)

117

Page 118: The full set of slides

There is another transformation — shearing — which can also

be described by these matrices. Shearing distorts a primitive

by “pushing” it in one direction, as shown:

x directionShearing in the

y directionShearing in the

The matrix for shearing is as follows, where ax and ay are the

factors for shearing in the x and y directions, respectively.

x′

y′

1

=

1 ax 0

ay 1 0

0 0 1

·

x

y

1

The general form for transformations derived from translation,

scaling, rotation, and shearing is:

x′

y′

1

=

r11 r12 tx

r21 r22 ty

0 0 1

·

x

y

1

where the rij correspond to some combination of rotation, scal-

ing, and shearing, and the t’s correspond to translation.

118

Page 119: The full set of slides

Recall again that the order of operations is important in apply-

ing transformations.

For example, if we have a point (or vector) V and wish to apply

translation, then scaling, then rotation, then translation again,

we would perform the operations as T (R(S(T (V ))))

A change in the order of operations may not produce the ex-

pected transformation.

This particular sequence is not uncommon — translate a prim-

itive to the origin, perform scaling and rotation, and translate

it back.

Note that, since the matrix has two zero elements and a con-

stant element (1) on the diagonal, a full matrix multiply is not

necessary.

In fact, the actual operations required are

x′ = r11x + r12y + tx

y′ = r21x + r22y + ty

which is simpler than a 3×3 matrix multiply in that it requires

only 4 multiply and 4 add operations.

119

Page 120: The full set of slides

Three dimensional transformations

All of the transformations we have seen have similar represen-

tations in 3 dimensions.

Translation:

x′

y′

z′

1

=

1 0 0 tx

0 1 0 ty

0 0 1 tz

0 0 0 1

·

x

y

z

1

Scaling:

x′

y′

z′

1

=

sx 0 0 0

0 sy 0 0

0 0 sz 0

0 0 1 1

·

x

y

z

1

Rotation — direct generalization is rotation about the z-axis:

x′

y′

z′

1

=

cos θ − sin θ 0 0

sin θ cos θ 0 0

0 0 1 0

0 0 0 1

·

x

y

z

1

120

Page 121: The full set of slides

Rotation about the x-axis:

x′

y′

z′

1

=

1 0 0 0

0 cos θ − sin θ 0

0 sin θ cos θ 0

0 0 0 1

·

x

y

z

1

Rotation about the y-axis:

x′

y′

z′

1

=

cos θ 0 sin θ 0

0 1 0 0

− sin θ 0 cos θ 0

0 0 0 1

·

x

y

z

1

Any arbitrary 3-dimensional rotation can be constructed from

the three rotation operations Rx, Ry, and Rz.

In fact, all the operations can be combined to give a general

transformation of the form

x′

y′

z′

1

=

r11 r12 r13 tx

r21 r22 r23 ty

r31 r32 r33 tz

0 0 0 1

·

x

y

z

1

121

Page 122: The full set of slides

OpenGL uses homogeneous coordinates in its internal represen-

tation of transformations.

OpenGL has several general purpose transformation functions,

as well as functions specifically for modeling and viewing. The

general purpose functions are:

void glMatrixMode(GLenum mode)

where the mode can be GL_MODELVIEW, GL_PROJECTION, or

GL_TEXTURE. (We will discuss textures later.)

Subsequent transformations affect only the specified matrix (only

one matrix can be modified at a time.)

void glLoadIdentity(void)

sets the current matrix to the identity matrix.

void glLoadMatrix*(const TYPE *matrix)

where *matrix is a pointer to a 16 element matrix of the ap-

propriate type. The function sets a matrix to the given values.

void glMultMatrix*(const TYPE *matrix)

This function multiplies the current matrix by the elements of

*matrix

The later two functions are not frequently used, because OpenGL

also provides easier ways to construct the basic matrices.

122

Page 123: The full set of slides

OpenGL has special functions for viewing and modeling. We

will look at the viewing matrices later; the modeling transfor-

mations are:

void glTranslate*(TYPE x, TYPE y, TYPE z)

which multiplies the current MODELVIEWmatrix by a translation

matrix ( which translates elements by amounts x, y and z).

void glRotate*(TYPE angle, TYPE x, TYPE y, TYPE z)

which multiplies the current matrix by a rotation matrix corre-

sponding to the specified angle (in degrees) about the vector

from the origin through (x,y,z).

void glScale*(TYPE x, TYPE y, TYPE z)

which multiplies the current matrix by the specified scaling ma-

trix.

Note that OpenGL constructs these matrices from their argu-

ments, and successive applications of these functions can be

used to construct quite elaborate transformations.

The tutorial transformation shows the use of the translate,

rotate, and scale transformations.

123

Page 124: The full set of slides

An example illustrating use of rotation matrices

/* Rotating cube, centered at the origin */

/* Demonstration of the use of homogeneous coordinate

* transformations and a simple data structure for

* representing (and animating) a solid figure */

#include <stdlib.h>

#include <GL/glut.h>

/* set up global 2d array of vertices for cube */

GLfloat vertices[][3] = {{-1.0,-1.0,-1.0},

{1.0,-1.0,-1.0}, {1.0,1.0,-1.0}, {-1.0,1.0,-1.0},

{-1.0,-1.0,1.0}, {1.0,-1.0,1.0}, {1.0,1.0,1.0},

{-1.0,1.0,1.0}};

/* define global variables for axis of rotation

* and 1d array of angles wrt. each axis */

static GLint axis = 2;

static GLfloat theta[] = {0.0,0.0,0.0};

124

Page 125: The full set of slides

/* draw a polygon via list of vertices */

void polygon(int a, int b, int c , int d)

{

glBegin(GL_POLYGON);

glColor4f (0.5, 0.0, 0.0,0.2);

glVertex3fv(vertices[a]);

glVertex3fv(vertices[b]);

glVertex3fv(vertices[c]);

glVertex3fv(vertices[d]);

glEnd();

}

void cube(void)

/* map vertices to faces */

{

polygon(0,3,2,1);

polygon(2,3,7,6);

polygon(0,4,7,3);

polygon(1,2,6,5);

polygon(4,5,6,7);

polygon(0,1,5,4);

}

125

Page 126: The full set of slides

void display(void)

{

/* display callback, clear frame buffer and z buffer,

rotate cube and draw, swap buffers */

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glLoadIdentity();

/* set up rotation matrices for rotation about axes */

glRotatef(theta[0], 1.0, 0.0, 0.0);

glRotatef(theta[1], 0.0, 1.0, 0.0);

glRotatef(theta[2], 0.0, 0.0, 1.0);

cube(); /* draw the cube */

glFlush();

glutSwapBuffers();

}

126

Page 127: The full set of slides

void mouse(int btn, int state, int x, int y)

{

/* mouse callback, selects a rotation axis */

if(btn==GLUT_LEFT_BUTTON && state == GLUT_DOWN)

axis = 0;

if(btn==GLUT_MIDDLE_BUTTON && state == GLUT_DOWN)

axis = 1;

if(btn==GLUT_RIGHT_BUTTON && state == GLUT_DOWN)

axis = 2;

}

void spinCube()

{

/* Idle callback, spin cube 2 degrees about

* selected axis */

theta[axis] += 2.0;

if( theta[axis] > 360.0 ) theta[axis] -= 360.0;

glutPostRedisplay();

}

127

Page 128: The full set of slides

void myReshape(int w, int h)

{

glViewport(0, 0, w, h);

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

if (w <= h)

glOrtho(-2.0,2.0,-2.0 * (GLfloat) h / (GLfloat) w,

2.0 * (GLfloat) h / (GLfloat) w, -10.0, 10.0);

else

glOrtho(-2.0 * (GLfloat) w / (GLfloat) h,

2.0 * (GLfloat) w / (GLfloat) h, -2.0, 2.0,

-10.0, 10.0);

glMatrixMode(GL_MODELVIEW);

}

128

Page 129: The full set of slides

void main(int argc, char **argv)

{

glutInit(&argc, argv);

/* uses both double buffering and z buffer */

glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB |

GLUT_DEPTH);

glutInitWindowSize(500, 500);

glutCreateWindow("rotating cube - mouse changes

rotation axis");

glutReshapeFunc(myReshape);

glClearColor (1.0, 1.0, 1.0, 0.0);

glutDisplayFunc(display);

glutIdleFunc(spinCube);

glutMouseFunc(mouse);

glutMainLoop();

}

129

Page 130: The full set of slides

Changes in coordinate systems

In computer graphics, as in other disciplines (robotics, image

processing, etc.) one of the uses of primitive transformations,

particularly translation and rotation, is to change the coordi-

nate system.

y0x

y1

1

y2

x

x0

2

In this example, coordinate system (x0, y0) would be used for

rotation of the first link of the arm, (x1, y1) would be used for

orienting the second link of the arm, and (x2, y2) would be used

for orienting the final link of the arm.

All of the motions at those joints would eventually have to be

translated into the “world coordinate system” of the device.

The idea of using different coordinate systems depending on the

“point of view” is extremely useful in computer graphics, where

a sub-scene can be described in its own coordinate space, and

then translated and/or rotated to the appropriate place in a

scene.

130

Page 131: The full set of slides

Recall that OpenGL uses homogeneous coordinates to specify

transformations.

The previous example (robot arm) indicated the potential use

of transformations in describing the motion of the arm; in fact,

if the two links shown are identical, then only one instance need

be created in its own “local” coordinate system, and it could be

translated, rotated, (and possibly scaled) to several instances in

the displayed picture.

Such transformations in the “model world” are called modeling

transformations and are extremely useful in model creation.

Suppose, for example, we wanted to take a primitive (say, the

second joint of the robot arm) and rotate it by 45◦, as shown:

0 1 2 3 4 5 6 7 8 9

01

34

2

This could be accomplished by translating the arm so the pivot

at (5, 0) was at the origin, rotating the arm 45◦, and translating

it back.

131

Page 132: The full set of slides

The required operations are:

1. Translate the arm with link from (5, 0) to (0, 0)

2. Rotate the arm 45◦ about (0, 0)

3. Translate the now rotated arm with link from (0, 0) to (5, 0)

The following OpenGL snippet would accomplish this (note the

order of the functions — most recently specified is the first

applied):

glMatrixMode(GL_MODELVIEW)

glLoadIdentity();

glTranslatef(5.0, 0.0, 0.0)

glRotatef(45.0, 0.0, 0.0, 1.0)

glTranslatef(-5.0, 0.0, 0.0)

The functions glTranslate*(x,y,z) behave much as expected,

causing a translation of amounts x, y, and z.

The functions glRotate*(a,x,y,z) rotates an object in a

counterclockwise direction by an angle a measured in degrees

about a vector from the origin to the point (x, y, z). The

example shows a rotation about the z-axis.

132

Page 133: The full set of slides

Note that the previous example, the arms were modeled in three

dimensions.

It is rather tedious to specify a complex 3-dimensional object

using OpenGL primitives directly, because of the small number

of primitives, and because they specify surfaces only. In general,

in 3 dimensions, both a surface “patch” must be specified, and

a normal to the surface.

The function glNormal*(x,y,z) sets a value for a normal

vector; this normal is used for every subsequent vertex until the

function is called again. (Normals are assigned to vertices.)

Normal vectors should have unit length.

Normal vectors are used when surfaces are illuminated, and if

an illumination model is not used, normals are not necessary.

We will discuss this in much more detail later.

Even without normals, it is tedious to construct a figure us-

ing surface patches only. Usually, the vertex array form of the

OpenGL functions are used for complex figures.

133

Page 134: The full set of slides

The following array contains the data required for the normals

for the rotating cube example:

GLfloat normals[][3] = {{-1.0,-1.0,-1.0},{1.0,-1.0,-1.0},

{1.0,1.0,-1.0}, {-1.0,1.0,-1.0}, {-1.0,-1.0,1.0},

{1.0,-1.0,1.0}, {1.0,1.0,1.0}, {-1.0,1.0,1.0}};

The code for constructing the polygons would now contain def-

initions of the normals to each surface as calls of the function

glNormal*()

glBegin(GL_POLYGON);

glNormal3fv(normals[a]);

glVertex3fv(vertices[a]);

glNormal3fv(normals[b]);

glVertex3fv(vertices[b]);

glNormal3fv(normals[c]);

glVertex3fv(vertices[c]);

glNormal3fv(normals[d]);

glVertex3fv(vertices[d]);

glEnd();

134

Page 135: The full set of slides

More OpenGL

We will look at the process of using a set of simple geometric

figures to build a more complex figure, but first we will look at

a few more features of OpenGL.

When using local coordinates to construct a component of a

model, the current model view matrix must often be replaced

temporarily by a local model view matrix.

OpenGL has a stack feature which allows several levels of model

view matrices to be pushed onto the stack, and popped off later.

The commands are:

glPushMatrix() glPopMatrix()

The projection matrix can also be pushed and popped.

In OpenGL, the default viewport is the entire pixel area of the

window that is opened. The command

glViewport(Glint x, Glint y, GLsizei w, GLsizei h)

can be used to change the size of the viewport. This is useful

to display several views of the same object in one window.

Remember that the model coordinate space is right

handed, and the viewing space is left handed.

135

Page 136: The full set of slides

Polygons have two sides — front and back. They may be ren-

dered differently, depending on which side faces the viewer.

Polygons whose vertices appear in counterclockwise order on

the screen are front-facing.

By default, both front and back faces are drawn the same way,

but this can be changed with glPolygonMode(face, mode).

face can be GL_FRONT, GL_BACK, or GL_FRONT_AND_BACK,

mode can be GL_POINT, GL_LINE, or GL_FILL,

Polygons can be culled, or discarded, with glCullFace(face)

where face is as defined above. Culling must be enabled with

glEnable(GL_CULL_FACE). (There are about 40 possible ar-

guments for glEnable(). There is also a glDisable().)

Reminder: At the time a polygon is created, the normals

should also be calculated. (Although they are not used now,

they will be important when we consider lighting models.)

136

Page 137: The full set of slides

The glu library contains functions for creating quadric surfaces

— spheres, cylinders, disks, etc.

Some primitives are the following (*qobj is a pointer to a

quadric object created by gluNewQuadric()):

gluSphere(*qobj, GLdouble radius, GLint slices, GLint

stacks) draws a sphere of given radius centered on the ori-

gin, with a number of slices around the z-axis, and stacks

along the z-axis.

gluCylinder(*qobj, GLdouble baseRadius, GLdouble topRadius,

GLdouble height, GLint slices, GLint stacks) draws

a cylinder along the z-axis with the base at z = 0 and the top

at z = height.

The quadric surfaces will be discussed in more detail later.

The glu library has a function which reverses a projection,

given a point and all the transformations which affected it. It

takes window coordinates and returns world coordinates.

gluUnProject( GLdouble winx, GLdouble winy, GLdouble

winz, const GLdouble modelMatrix[16], const GLdouble

projMatrix[16], const GLint viewport[4], GLdouble

*objx, GLdouble *objy, GLdouble *objz)

137

Page 138: The full set of slides

The GLUT library has functions to render some geometric solids,

and wireframe (outline) solids as well:

glutSolidSphere(GLdouble radius, GLint slices, GLint

stacks)

and glutWireSphere()

glutSolidCube(GLdouble size)

and glutWireCube()

glutSolidCone(GLdouble radius, GLdouble height, GLint

slices, GLint stacks)

and glutWireCone()

The GLUT library also has functions to create pop-up menus.

These are useful for choice type interaction.

The basic functions are

glutCreateMenu(menu_callback(int id))

which defines the menu (and the callback which implements the

menu functions — id is an identifier for the menu item),

glutAddMenuEntry(char* name, int id)

and glutAttachMenu(int button)

which attaches the menu function to some button.

It is also possible to create submenus. (See the glut documen-

tation.)

138

Page 139: The full set of slides

An example illustrating use of the model view

(From the reference text Interactive Computer Graphics: A

Top-Down Approach with OpenGL, by Edward Angel)

/* Robot program. Cylinder for base, scaled cube for arms*/

/* Uses instance transformation to define parts (symbols)*/

/* The cylinder is a quadric object from the GLU library */

/* The cube is also obtained from GLU */

#include <GL/glut.h>

/* Define the constants (easier to change them) */

#define BASE_HEIGHT 2.0

#define BASE_RADIUS 1.0

#define LOWER_ARM_HEIGHT 5.0

#define LOWER_ARM_WIDTH 0.5

#define UPPER_ARM_HEIGHT 5.0

#define UPPER_ARM_WIDTH 0.5

typedef float point[3];

static GLfloat theta[] = {0.0,0.0,0.0};

static GLint axis = 0;

GLUquadricObj *p; /* pointer to quadric object */

139

Page 140: The full set of slides

/* Define the three parts */

/* Note use of push/pop to return modelview matrix

to its state before functions were entered and use

rotation, translation, and scaling to create instances

of symbols (cube and cylinder) */

void base()

{

glPushMatrix();

/* rotate cylinder to align with y axis */

glRotatef(-90.0, 1.0, 0.0, 0.0);

/* cylinder aligned with z axis, render with

5 slices for base and 5 along length */

gluCylinder(p, BASE_RADIUS, BASE_RADIUS,

BASE_HEIGHT, 5, 5);

glPopMatrix();

}

140

Page 141: The full set of slides

void upper_arm()

{

glPushMatrix();

glTranslatef(0.0, 0.5*UPPER_ARM_HEIGHT, 0.0);

glScalef(UPPER_ARM_WIDTH, UPPER_ARM_HEIGHT,

UPPER_ARM_WIDTH);

glutWireCube(1.0);

glPopMatrix();

}

void lower_arm()

{

glPushMatrix();

glTranslatef(0.0, 0.5*LOWER_ARM_HEIGHT, 0.0);

glScalef(LOWER_ARM_WIDTH, LOWER_ARM_HEIGHT,

LOWER_ARM_WIDTH);

glutWireCube(1.0);

glPopMatrix();

}

141

Page 142: The full set of slides

void display(void)

{

/* Develop ModelView Matrix as we traverse structure */

glClear(GL_COLOR_BUFFER_BIT);

glLoadIdentity();

glColor3f(1.0, 0.0, 0.0);

glRotatef(theta[0], 0.0, 1.0, 0.0);

base();

glTranslatef(0.0, BASE_HEIGHT, 0.0);

glRotatef(theta[1], 0.0, 0.0, 1.0);

lower_arm();

glTranslatef(0.0, LOWER_ARM_HEIGHT, 0.0);

glRotatef(theta[2], 0.0, 0.0, 1.0);

upper_arm();

glFlush();

glutSwapBuffers();

}

142

Page 143: The full set of slides

void mouse(int btn, int state, int x, int y)

{

/* left button increase joint angle, right decreases */

if(btn==GLUT_LEFT_BUTTON && state == GLUT_DOWN)

{

theta[axis] += 5.0;

if( theta[axis] > 360.0 ) theta[axis] -= 360.0;

}

if(btn==GLUT_RIGHT_BUTTON && state == GLUT_DOWN)

{

theta[axis] -= 5.0;

if( theta[axis] < 360.0 ) theta[axis] += 360.0;

}

display();

}

void menu(int id)

{

/* menu selects rotation axis or whether to quit*/

if(id == 1 ) axis=0;

if(id == 2) axis=1;

if(id == 3 ) axis=2;

if(id ==4 ) exit();

}

143

Page 144: The full set of slides

void myReshape(int w, int h)

{

glViewport(0, 0, w, h);

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

if (w <= h)

glOrtho(-10.0,10.0, -5.0 * (GLfloat)h / (GLfloat)w,

15.0 * (GLfloat) h / (GLfloat) w, -10.0, 10.0);

else

glOrtho(-10.0 * (GLfloat)w / (GLfloat)h,

10.0 * (GLfloat)w / (GLfloat)h, -5.0,

15.0, -10.0, 10.0);

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

}

void myinit()

{

glClearColor(1.0, 1.0, 1.0, 1.0);

glColor3f(1.0, 0.0, 0.0);

p=gluNewQuadric(); /* allocate quadric object */

gluQuadricDrawStyle(p, GLU_LINE); /* draw as wireframe*/

}

144

Page 145: The full set of slides

void

main(int argc, char **argv)

{

glutInit(&argc, argv);

glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB

| GLUT_DEPTH);

glutInitWindowSize(500, 500);

glutCreateWindow("robot");

myinit();

glutReshapeFunc(myReshape);

glutDisplayFunc(display);

glutMouseFunc(mouse);

glutCreateMenu(menu);

glutAddMenuEntry("base", 1);

glutAddMenuEntry("lower arm", 2);

glutAddMenuEntry("upper arm", 3);

glutAddMenuEntry("quit", 4);

glutAttachMenu(GLUT_MIDDLE_BUTTON);

glutMainLoop();

}

145

Page 146: The full set of slides

In the previous example, the construction of the robot arm was

a simple hierarchy.

Other structures can be used just as effectively. For example, a

tree structure might be a good representation for a more com-

plex structure.

A robot with two arms might be constructed as a trunk, with

two jointed arms attached, and with a set of fingers.

Finger FingerFingerFinger

LowerArm

ArmUpper

ArmUpper

ArmLower

TRUNK

See the program figuretree.c for a similar example.

146

Page 147: The full set of slides

Projections and their transformations

We have looked a little bit at modeling 3-dimensional figures in

a 3-dimensional world, or view. The final image, however, will

be displayed on a 2-dimensional surface.

The transformation from the 3-dimensional model space to the

2-dimensional viewing space is accomplished by projecting the

model onto a plane.

Here is where our “virtual camera” model offers a good analogy.

scene

view planecamera

We are interested in the projection of the scene unto the view-

plane. This is often called the viewing transformation, and is

analogous to setting the position of the camera (and the type

of lens, etc.)

Things are really a bit different, however, because with com-

puter graphics we have rather more freedom than we have with

a camera.

147

Page 148: The full set of slides

For example, we can define a number of different kinds of pro-

jections. (Very many kinds of projections have been defined,

particularly by mapmakers, since they must project inherently

3-dimensional surfaces on a 2-dimensional surface, and preserve

some sets of useful information.)

There are two basic projections we will discuss — parallel pro-

jections and perspective projections.

Parallel projections

The basic property of a parallel projection is that features are

projected by parallel rays unto the viewing surface.

There are again two types of parallel projections — projections

in which the projection is parallel to the normal of the view-

ing surface orthographic projections and projections where the

projection rays are not normal to the surface oblique projec-

tions.

The most common types of orthographic projections are “archi-

tectural projections” (usually just called orthographic projec-

tions) where the projections are parallel to the principal axes.

Normally, it would be annotated with measurements, etc.

With some practice, it is not difficult to capture all the construc-

tion details of a house, for example, with this type of projection.

148

Page 149: The full set of slides

The following shows two types of orthographic projections —

an isometric projection and an equivalent orthonormal (archi-

tectural) projection:

Front view

Side view

Top view

The isometric projection is a projection in which the object

is at the same angle to all three principal axes. (This has the

property that all 3 axes can be measured on the same scale.)

It is a special case of an axonometric orthographic projection.

The center drawing of the house above is an example of this.

149

Page 150: The full set of slides

Oblique projections

Oblique projections are the most general parallel views. In an

oblique projection, the projectors make some arbitrary angle

with the projection plane.

These views are somewhat unnatural, but can be used to convey

more information than orthographic projections.

The human eye, and most other physical viewing devices, have a

lens fixed with respect to (and usually parallel to) the viewing

plane. This is, in fact, what makes oblique projections seem

unnatural.

The old “press cameras” often had a feature where the film plane

could be tilted relative to the lens. This allowed an oblique

projection, albeit not a parallel projection.

From the point of view of a graphics system, as long as the pro-

jection can be described using primitive transformations, there

is little difference among all the parallel projections.

The primary consideration is how to specify the appropriate

parameters to the graphics system to produce the required view.

150

Page 151: The full set of slides

Perspective projections

In a perspective projection, the projecting rays are not parallel,

but instead pass through one or more vanishing points or points

at infinity.

This is a (somewhat extreme) example of one point perspective.

Note that the lines which should be parallel actually converge

in the “far distance.” This creates an illusion of depth in a

2-dimensional figure.

Two point perspective would use 2 vanishing points, roughly

orthogonal to each other in the “model world.”

In a perspective projection, we need to specify a point as the

center of projection; for parallel projections we need to specify

a direction of projection.

151

Page 152: The full set of slides

The viewing surface

The plane onto which the image is projected is usually called

the view plane. It has coordinates (u, v, n) orthogonal to each

other.

It is defined by the view reference point or VRP, and the view

plane normal or VPN and points along the n axis.

The view plane corresponds to the “film” in our virtual camera.

We also need to describe the orientation of the “film” or view-

port in the view plane. This is done by specifying a vector called

the view up vector (VUP). The projection of the VUP vector

parallel to VPN in the plane defines the v axis in the plane.

This, in effect, orients the camera. The u axis is perpendicular

to v and n.

Defining minimum and maximum values of u and v determines

the viewing area.

VRP

VPNn

u

v

CW

VUP

viewplane

min min(u ,v )

max max(u ,v )

152

Page 153: The full set of slides

In many graphics systems, there are functions which set the

viewing parameters explicitly, using functions like:

set_view_reference_point(x, y, z)

set_view_plane_normal(nx, ny, nz)

set_view_up(vx, vy, vz)

In OpenGL, there is a convenient function which is rather more

direct, and uses the virtual camera model explicitly. Basically,

the position of the camera (or eye), and the position of the

object are specified, These specify the VPN and a VRP. In

fact, these are specified in the GL ModelView transformation.

The orientation of the camera also needs to be specified (VUP).

gluLookAt(eyex, eyey, eyez, atx, aty, atz, upx, upy,

upz)

eyex, eyey, and eyez specify the camera (or eye) position,

atx, aty, and atz specify the object center.

upx, upy, and upz specify VUP.

The glu.. functions are not OpenGL primitives, but are part

of a standard library which forms part of the OpenGL system.

(GLUT is another library which provides user interaction.)

The tutorial projection shows the use of the projection func-

tions for orthogonal and perspective transformations.

153

Page 154: The full set of slides

Clipping

Viewing involves both projection and clipping. In three dimen-

sions, we must clip against a 3-dimensional view volume.

For the parallel projection case, the additions to the 2-dimensional

clipping we discussed earlier are a front clipping plane and a

back clipping plane, forming a clipping rectangular volume.

Backclipping

planeplaneclipping

Front

planeView

VPN

The perspective projection case is a bit different. Here, the front

and back clipping planes are parallel to the view plane, but are

of different sizes.

Back

planeclipping

Front

planeView

VPN

planeclipping

The shape of the bounding figure in this case is a frustum. This

is a pyramid with the point cut off.

154

Page 155: The full set of slides

Normally, the clipping would be done on a figure transformed

so the VPN lies along the Z axis.

Parallel projections in OpenGL

In OpenGL, there is only one parallel viewing function, the

orthographic viewing function

glOrtho(xmin, xmax, ymin, ymax, zmin, zmax), or

glOrtho(left, right, bottom, top, near, far)

where (xmin, ymin, zmin) are the coordinates of the lower front

corner of the rectangular volume, and (xmax, ymax, zmax) are

the coordinates of the back top corner.

(We have already used this function in our 2-dimensional graph-

ics examples.)

For a view volume consisting of a 2x2x2 cube centered at the

origin, the function calls would be

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);

155

Page 156: The full set of slides

Perspective projections in OpenGL

There are two functions for perspective projections.

glFrustum(left, right, bottom, top, near, far)

defines a frustum similar to the rectangular volume for glOrtho(..).

Note that near and far should be positive.

lefttop

bottom

right

near

far

A typical use would be

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

glFrustum(-1.0, 1.0, -1.0, 1.0, 1.0, 10.0);

Here the front clipping plane is (-1, -1, 1) to (1, 1, 1). The rear

clipping plane is 9 units beyond the front clipping plane.

While conceptually easy, glFrustum() is difficult to use.

See the tutorial projection for an example of its use.

156

Page 157: The full set of slides

An alternate way to determine the view volume is by specifying

an angular field (in the y direction, in this case), an aspect ratio

(width/height) and the position of the front and back clipping

planes.

This is not an OpenGL primitive, but is available in the glu

library.

gluPerspective(fovy, aspect, near, far)

near

far

fovy h

w

With this function, it is necessary to pick appropriate values for

the field of view, or the view looks distorted.

With both gluPerspective() and glFrustum() rotations

and translations can be applied to change the default orientation

of the viewing volume.

Without such transformations, the viewpoint remains at the

origin, with the line of sight along the negative z-axis.

157

Page 158: The full set of slides

Simple orthogonal projection

Let us consider a projection onto a plane perpendicular to the

z-axis, at z = 0.

in this case, points retain their x and y values, and the equations

of projection are

xp = x, yp = y, and zp = 0,

In homogeneous coordinates,

xp

yp

zp

1

=

1 0 0 0

0 1 0 0

0 0 0 0

0 0 0 1

·

x

y

z

1

We can use the transformation matrices to change the projec-

tion matrix to whatever direction we wish.

Alternately, we can orient the image, scale it, perform clipping,

and apply this projection, then transform it again.

158

Page 159: The full set of slides

Simple perspective projection

We will again consider the case where the projection is on a

plane on the z-axis, at a point (0, 0, zp)

y

z

x

(x,z)

(a) (b)

x

z

p(x ,−d)(x,y,z)

z = −d (x ,y ,z )pp p

Here, figure (b) is an orthogonal projection of (a) looking di-

rectly along the y axis.

By similar triangles, x/z = xp/d

Similarly, y/z = yp/d

So, xp =x

z/dand yp =

y

z/d

This is not an affine transformation, nor is it reversible — all

points along a projector map into the same point.

(We will find another transformation later which is reversible.)

159

Page 160: The full set of slides

It is possible, however, to find a matrix which accomplishes

the perspective projection on the z-axis. We must relax the

condition that W = 1 in our homogeneous coordinate system,

however.

In order to get a point, we must “renormalize” our point repre-

sentation to set W = 1 again.

The transformation matrix

M =

1 0 0 0

0 1 0 0

0 0 −1 0

0 0 1/d 0

Transforms the point (x, y, z, 1) into (x, y,−z, z/d)

Renormalizing, this is

(x

z/d,

y

z/d,−d, 1)

which is the required transformation.

Again, it is possible to transform this matrix to project in an

arbitrary direction using the rotation transformations discussed

earlier.

160

Page 161: The full set of slides

Clipping in homogeneous coordinates

It is important for efficiency not to have to change the repre-

sentation very often, since the view transformations are applied

to each vertex in the image.

Therefore, it would be useful to

1. have a single “combined transformation”

2. clip in a standard, or canonical view volume.

This would be possible if we could transform a perspective pro-

jection into a parallel projection.

In fact, such a perspective-normalization matrix is derivable:

M =

1 0 0 0

0 1 0 0

0 0 11+zmin

zmin1+zmin

0 0 −1 0

0 > zmin > −1

Note that this matrix similar in form to the perspective trans-

formation matrix but is is non-singular.

This transforms a perspective mapping into an orthogonal map-

ping, so that clipping can be done inside a canonical volume.

161

Page 162: The full set of slides

How is a projection matrix constructed?

The following is general, and allows oblique projections:

For parallel projections:

1. Translate the VRP to the origin.

2. Rotate VRC such that the n axis (VPN) maps to the z

axis, the u axis becomes the x axis, and the v axis becomes

the y axis.

3. Shear so the direction of projection is parallel to the z axis.

4. Translate into the canonical view volume.

5. Scale to fit inside the canonical volume.

Clipping can then be applied on the canonical volume.

Each of those steps is characterized by a transformation matrix,

and the required transformation is the product of all of those

simple transformation matrices.

This transformation is often called a normalizing transforma-

tion, and the set of points output (after clipping) are said to be

in normalized device coordinates (NDC).

The NDC are a device independent representation which are

then transformed into the actual points on the display device.

162

Page 163: The full set of slides

For perspective projections:

1. Translate the VRP to the origin.

2. Rotate VRC such that the n axis (VPN) maps to the z

axis, the u axis becomes the x axis, and the v axis becomes

the y axis.

3. Translate so the center of projection (COP) given by the

projection reference point (PRP) is at the origin.

4. Shear so the center line of the view volume is parallel to

the z axis.

5. Scale to fit inside the canonical view volume.

6. Apply the perspective normalization transformation.

Again, each step is characterized by a transformation matrix,

and the required transformation is the product of all of those

simple transformation matrices.

Following the application of this transformation to each vertex,

clipping can be done against the canonical volume for parallel

projections.

163

Page 164: The full set of slides

The viewing pipeline

What happens, in OpenGL, in order to view an image?

eye

Normalizeddevice

coordinates

coordinates

PerspectiveDivision

(Renormalization)

MatrixProjection

coordinatesclip

object

coordinates

coordinates

Window

ModelView

Matrix

TransformationViewport

The objects themselves are described in their own coordinate

space, and placed in the global model world by the appropriate

model view transformation.

The projection matrix maps the model world into the canonical

clipping volume (this transformation is not affine) and then the

points are renormalized, and transformed for display.

Following are some OpenGL examples of the use of transforma-

tions for modeling and viewing.

164

Page 165: The full set of slides

An example illustrating use of both modeling and

viewing transformations

/* planet.c

* Copyright (c) 1993-1997, Silicon Graphics, Inc.

* ALL RIGHTS RESERVED

*

* Program shows composition of modeling transformations

* to draw translated and rotated models.

* Interaction: pressing the d and y keys (day and year)

* alters the rotation of the planet around the sun.

* Modified to change the y viewing height with v and V.

*/

#include <GL/glut.h>

#include <stdlib.h>

static int year = 0, day = 0;

static float viewy = 0.0;

void init(void)

{

glClearColor (1.0, 1.0, 1.0, 0.0);

glShadeModel (GL_FLAT);

}

165

Page 166: The full set of slides

void display(void)

{

glClear (GL_COLOR_BUFFER_BIT);

glColor3f (0.0, 0.0, 0.0);

glPushMatrix();

glutWireSphere(1.0, 20, 16); /* draw sun */

glRotatef ((GLfloat) year, 0.0, 1.0, 0.0);

glTranslatef (2.0, 0.0, 0.0);

glRotatef ((GLfloat) day, 0.0, 1.0, 0.0);

glutWireSphere(0.2, 10, 8); /* draw planet */

glPopMatrix();

glutSwapBuffers();

}

166

Page 167: The full set of slides

void keyboard (unsigned char key, int x, int y)

{

switch (key) {

case ’d’:

day = (day + 10) % 360;

glutPostRedisplay();

break;

case ’D’:

day = (day - 10) % 360;

glutPostRedisplay();

break;

case ’y’:

year = (year + 5) % 360;

glutPostRedisplay();

break;

case ’Y’:

year = (year - 5) % 360;

glutPostRedisplay();

break;

case ’v’:

viewy = (viewy + 1.0);

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

167

Page 168: The full set of slides

gluLookAt (0.0, viewy, 5.0, 0.0, 0.0, 0.0,

0.0, 1.0, 0.0);

glutPostRedisplay();

break;

case ’V’:

viewy = (viewy - 1.0);

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

gluLookAt (0.0, viewy, 5.0, 0.0, 0.0, 0.0,

0.0, 1.0, 0.0);

glutPostRedisplay();

break;

case 27:

exit(0);

break;

default:

break;

}

}

168

Page 169: The full set of slides

void reshape (int w, int h)

{

glViewport (0, 0, (GLsizei) w, (GLsizei) h);

glMatrixMode (GL_PROJECTION);

glLoadIdentity ();

gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0,

20.0);

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

gluLookAt (0.0, viewy, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0,

0.0);

}

169

Page 170: The full set of slides

int main(int argc, char** argv)

{

glutInit(&argc, argv);

glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);

glutInitWindowSize (500, 500);

glutInitWindowPosition (100, 100);

glutCreateWindow (argv[0]);

init ();

glutDisplayFunc(display);

glutReshapeFunc(reshape);

glutKeyboardFunc(keyboard);

glutMainLoop();

return 0;

}

170

Page 171: The full set of slides

A second example illustrating use of both modeling

and viewing transformations

The program scene.c shows the use of three glut functions

(the cone, torus, and sphere) to construct a simple scene.

It also allows the viewer to interact with the scene to, for ex-

ample, change the view position.

#include <GL/glut.h>

#include <stdlib.h>

static float viewx = 0.0;

int axes = 0, cone = 1, torus = 1, sphere = 1;

static void init (void)

{

// select clearing color

glClearColor (0.0, 0.0, 0.0, 0.0);

glShadeModel (GL_FLAT);

}

171

Page 172: The full set of slides

static void display (void)

{

glClear (GL_COLOR_BUFFER_BIT);

if (axes)

{

// draw axes

glBegin(GL_LINES);

glColor3f (1.0, 0.0, 0.0);

glVertex3f(-2.0, 0.0, 0.0);

glVertex3f( 2.0, 0.0, 0.0);

glColor3f (0.0, 1.0, 0.0);

glVertex3f( 0.0,-2.0, 0.0);

glVertex3f( 0.0, 2.0, 0.0);

glColor3f (0.0, 0.0, 1.0);

glVertex3f( 0.0, 0.0,-2.0);

glVertex3f( 0.0, 0.0, 2.0);

glEnd();

}

172

Page 173: The full set of slides

if (torus) // default torus center is on Z axis

{

glPushMatrix ();

glColor3f (1.0, 0.0, 0.0);

glTranslatef (0.0, 0.5, 0.0);

glRotatef (90.0, 1.0, 0.0, 0.0);

glutWireTorus (0.275, 0.85, 30, 30);

glPopMatrix ();

}

if (cone) // default cone is along Z axis

{

glColor3f (0.0, 1.0, 0.0);

glPushMatrix ();

glTranslatef (0.0, -0.5, 0.0);

glRotatef (270.0, 1.0, 0.0, 0.0);

glutWireCone (1.0, 2.0, 30, 30);

glPopMatrix ();

}

173

Page 174: The full set of slides

if (sphere) // default sphere is at origin

{

glColor3f (0.0, 0.0, 1.0);

glPushMatrix ();

glTranslatef (0.75, 0.0, -1.0);

glutWireSphere (1.0, 30, 30);

glPopMatrix ();

}

glutSwapBuffers();

}

static void reshape(int w, int h)

{

glViewport (0, 0, (GLsizei) w, (GLsizei) h);

glMatrixMode (GL_PROJECTION);

glLoadIdentity();

gluPerspective(60.0,(GLfloat) w/(GLfloat) h,1.0,20.0);

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

gluLookAt (viewx,0.0,4.5,0.0,0.0,0.0,0.0,1.0,0.0);

}

174

Page 175: The full set of slides

static void

key(unsigned char k, int x, int y)

{

switch (k) {

case ’a’:

case ’A’:

axes = !axes;

break;

case ’s’:

case ’S’:

sphere = !sphere;

break;

case ’t’:

case ’T’:

torus = !torus;

break;

case ’c’:

case ’C’:

cone = !cone;

break;

175

Page 176: The full set of slides

case ’v’:

viewx = (viewx + 1.0);

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

gluLookAt (viewx,0.0,4.5,0.0,0.0,0.0,0.0,1.0,0.0);

break;

case ’V’:

viewx = (viewx - 1.0);

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

gluLookAt (viewx,0.0,4.5,0.0,0.0,0.0,0.0,1.0,0.0);

break;

case 27: /* Escape */

exit(0);

break;

default:

return;

}

glutPostRedisplay();

}

176

Page 177: The full set of slides

/* Main Loop

* Open window with initial window size, title bar,

* RGBA display mode, and handle input events.

*/

int main(int argc, char** argv)

{

glutInit(&argc, argv);

glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);

glutInitWindowSize (500, 500);

glutCreateWindow (argv[0]);

init ();

glutReshapeFunc (reshape);

glutDisplayFunc(display);

glutKeyboardFunc(key);

glutMainLoop();

return 0; // ANSI C requires main to return int.

}

177

Page 178: The full set of slides

Color

One of the primary advantages of raster graphics devices is the

general availability of color. The present display technologies

(CRT and LCD screens) are capable of a color resolution which

is a very good match to the range of colors visible to the human

eye.

Color printers with good resolution are also available at rela-

tively low cost (color inkjet printers) and other color devices

(laser and dye diffusion printers) are rapidly becoming cheaper

and better.

We have only discussed color very briefly, in the context of 2-D

graphics, and some limited 3-D applications.

OpenGL has two color modes — the RGBA mode we have

already seen briefly, and the color index mode.

In the RGBA mode, a color is specified by three intensities

(for the Red, Green, and Blue components of the color) and

optionally a fourth value, Alpha, which is the transparency.

The function glColor4f(red, green, blue, alpha)

maps available red, green, and blue intensities onto (0.0, 1.0)

where 0.0 means that the color component is absent ((0.0, 0.0,

0.0) is black) and 1.0 is a saturated color ((1.0, 1.0, 1.0) is

white.)

178

Page 179: The full set of slides

The number of bits used to represent each color depends on the

particular computer system (actually, the “graphics board” in

the system.)

Currently, graphics boards usually have several Mbytes of mem-

ory, and using 24 or 32 bits for color (8 to 10 bits per color, R,

G, or B) is common. (This allows 16M to 1G individual colors

to be displayed.)

Not long ago, 8 to 16 bits of color was usual, permitting only

256 to 64K individual colors. In order to increase the range

of colors displayed, a color lookup table was often used. In

the table, colors would be represented by, say, 24 bits, and the

“color” specified by the color word (say, n bits — 8 to 12) would

be an index to this table.

Only 2n colors could be displayed simultaneously, but the color

table could be altered to give a different palate of colors for each

application.

OpenGL also supports this mode, called color index mode.

The command glIndexf(cindex) sets the current color index

to cindex.

Analogous to glClearColor() there is a corresponding

glClearIndex(clearindex) which sets the clearing color to

clearindex.

179

Page 180: The full set of slides

OpenGL has no function to set a color lookup table, but GLUT

has the function

glutSetColor(GLint index, GLfloat red, GLfloat green,

GLfloat blue) which sets the color at index to the intensities

specified by red, green, and blue.

The function getIntegerv() with arguments GL_RED_BITS,

GL_GREEN_BITS, GL_BLUE_BITS, GL_ALPHA_BITS, and

GL_INDEX_BITS returns the number of bits for the identified

entity.

Color-index mode does not support transparency.

Choosing between color modes

To a large extent, this depends on the particular system you are

using. In general, RGBA mode provides more flexibility, and is

well supported by current hardware.

Color index mode may be useful when:

• porting or modifying an existing program using color-index

mode

• only a small number of bitplanes are available (e.g., 8 bits

— possibly 3 for red, 3 for green, and 2 for blue, giving a

very small range for each color)

• drawing layers and color-map animation are used.

180

Page 181: The full set of slides

Shading models

OpenGL has two shading models; primitives are drawn with a

single color (flat shading) or with a smooth color variation from

vertex to vertex (smooth shading, or Gouraud shading).

The shading mode is specified with glShadeModel(mode)where

mode is either GL_SMOOTH or GL_FLAT.

In RGBA mode, the color variation is obtained by interpolating

the RGB values in both directions, (bilinear interpolation) and

is as smooth as possible with the given number of bit planes.

The interpolation is on each of the R, G, and B components,

and is in the horizontal and vertical directions.

In color index mode, the index values are interpolated, so the

color table must be loaded with a set of smoothly changing

colors.

For flat shading, the color of a single vertex determines the color

of the polygon.

For the situation where lighting elements are present, a more

elaborate shading algorithm can be used. (This will be discussed

later.)

181

Page 182: The full set of slides

The rotating cube example shows an interesting example of

smooth shading.

An array of colors can be defined for the vertices, and used

much like the vertex coordinates (the program cubev uses this

code.)

glBegin(GL_POLYGON);

glColor3fv(colors[a]);

glNormal3fv(normals[a]);

glVertex3fv(vertices[a]);

glColor3fv(colors[b]);

glNormal3fv(normals[b]);

glVertex3fv(vertices[b]);

glColor3fv(colors[c]);

glNormal3fv(normals[c]);

glVertex3fv(vertices[c]);

glColor3fv(colors[d]);

glNormal3fv(normals[d]);

glVertex3fv(vertices[d]);

glEnd();

182

Page 183: The full set of slides

Transparency

So far, we have ignored the alpha term.

The natural interpretation of alpha is opacity. When alpha

is large, the polygon is opaque; when small, it is translucent.

For alpha to have any meaning, blending must be set.

This is done with glEnable(GL_BLEND)

Next, the way the blending is to be done is specified. Basically,

the colors of the incoming polygon (the source) are combined

with the currently stored pixel value (the destination) to give a

resultant pixel color as follows:

(RsSr +RdDr, GsSg +GdDg, BsSb +BdDb, AsSa + AdDa)

(Sr, Sg, Sb) and (Dr, Dg, Db) are blending factors for the source

and destination red, green, and blue components, respectively.

They have values between 0 and 1, and the resulting pixel values

are clamped to [0,1].

The blending factors are set with the function

glBlendFunc(GLenum sfactor, GLenum dfactor)

The following table gives values for sfactor and dfactor:

183

Page 184: The full set of slides

Constant factor blend factor

GL_ZERO s or d (0, 0, 0, 0)

GL_ONE s or d (1, 1, 1, 1)

GL_DST_COLOR s (Rd, Gd, Bd, Ad)

GL_SRC_COLOR d (Rs, Gs, Bs, As)

GL_ONE_MINUS_DST_COLOR s (1, 1, 1, 1)− (Rd, Gd, Bd, Ad)

GL_ONE_MINUS_SRC_COLOR d (1, 1, 1, 1)− (Rs, Gs, Bs, As)

GL_SRC_ALPHA s or d (As, As, As, As)

GL_DST_ALPHA s or d (Ad, Ad, Ad, Ad)

GL_ONE_MINUS_SRC_ALPHA s or d (1, 1, 1, 1)− (As, As, As, As)

GL_ONE_MINUS_DST_ALPHA s or d (1, 1, 1, 1)− (Ad, Ad, Ad, Ad)

GL_SRC_ALPHA_SATURATE s (f, f, f, 1),

f = min(As, 1− Ad)

Blending can be disabled with glDisable(GL_BLEND)

The same effect can be achieved by using GL_ONE for the source

and GL_ZERO for the destination.

(In fact, this is the default setting when blending is enabled!)

184

Page 185: The full set of slides

Sample Uses of Blending:

• To draw a picture composed half of one image and half

of another, equally blended, first set the source factor to

GL_ONE and the destination factor to GL_ZERO, and draw

the first image. Then set the source factor to GL_SRC_ALPHA

and destination factor to GL_ONE_MINUS_SRC_ALPHA, and

draw the second image with alpha equal to 0.5.

This probably is the most common blending operation.

• The blending functions using the source or destination col-

ors GL_DST_COLOR or GL_ONE_MINUS_DST_COLOR for the

source factor and GL_SRC_COLOR or GL_ONE_MINUS_SRC_COLOR

for the destination factor effectively allow modification of

each color component individually.

• Suppose we wish to draw a picture with three translucent,

overlapping surfaces, covering a solid background. Assume

the farthest surface transmits 80 % of the color behind it,

the next transmits 50 %, and the closest transmits 90 %.

First draw the solid background, then set the blending fac-

tors to GL_SRC_ALPHA (source) and GL_ONE_MINUS_SRC_ALPHA

(destination). Next, draw the farthest surface with an

alpha of 0.2, then the middle surface with an alpha of

0.5, and finally the closest surface with an alpha of 0.1.

185

Page 186: The full set of slides

The program alpha.c shows a simple application of blending.

Two intersecting triangle are drawn, and the overlap is blended.

The pertinent code for blending is:

static void init(void)

{

glEnable (GL_BLEND);

glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glShadeModel (GL_FLAT);

glClearColor (0.0, 0.0, 0.0, 0.0);

}

Note that only the state of the system is changed; the drawing

functions are used normally.

The code example alpha toggles which of two overlapping tri-

angles is on top.

186

Page 187: The full set of slides

The code to draw one triangle is:

static void drawLeftTriangle(void)

{

/* draw yellow triangle on LHS of screen */

glBegin (GL_TRIANGLES);

glColor4f(1.0, 1.0, 0.0, 0.75);

glVertex3f(0.1, 0.9, 0.0);

glVertex3f(0.1, 0.1, 0.0);

glVertex3f(0.7, 0.5, 0.0);

glEnd();

}

Similar code draws the right (cyan) triangle.

Other effects can be obtained with different blending modes.

187

Page 188: The full set of slides

Hierarchical modeling

OpenGL provides a basic set of primitives for rendering and

displaying 2-D and 3-D graphical output. Standard libraries

(GLU and GLUT, for example) provide additional functionality

including more complex geometrical models, and interaction

with the underlying windowing system.

Generally, a graphics package is used in some application do-

main (e.g., logic circuit layout) in which the graphical entities

have a meaning independent of their graphical properties, and

the various graphical entities may have relations among them

which have graphical or other manifestations.

Different applications may well represent information graphi-

cally, but the underlying graphics system should not be con-

cerned with what the various graphical entities represent in the

applications domain.

For example, in an organizational chart, some relations may be

inferred from the connectivity of components; others by color or

other attributes. (E.g., in the following organizational chart, the

management hierarchy may be inferred from the connectivity

of the graph, the job class by the color.)

Note that the application, not the graphical representation, is

concerned with the meaning of the graphics primitives.

188

Page 189: The full set of slides

VP R&D

President

Manager(Sales)(Accounting)

Manager

VP sales

ManagerDevelopment

EngineeringStaff Accountants Sales staff

Note that there is no inherent meaning in the graphical struc-

tures (colored boxes and interconnections).

It is up to the application how to derive meaning from or use

information obtained from interactions with the graphical struc-

ture.

189

Page 190: The full set of slides

Geometric models

There are, however, some parts of a graphical system which are

generally considered to be part of the graphics system. Those

are the geometric models used to represent entities in the ap-

plications domain. Some of the relations between the entities

may also have graphical representations.

Recall the example of the robot arm. In this case, the vari-

ous angles used to represent the positions of the arm segments,

and the dimensions of the segments themselves, belong to the

graphical domain. (They also have a meaning in the applica-

tions domain.)

We saw that the robot arm could be built in as hierarchical man-

ner. Each arm was constructed from a simple volume primitive

(a cube), then sized and positioned with a set of transforma-

tions. The hierarchy was robot — arm — transformed primi-

tive.

In general, a hierarchical model is often useful for the geometric

modeling of complex figures.

The hierarchy itself may contain other useful information (e.g.,

connectivity).

190

Page 191: The full set of slides

The hierarchy is useful for several reasons:

• modular construction of the object

• storage economy — references to repeated objects, rather

than duplicating object definitions

• propagation of modifications to components — components

are defined once and instantiated by reference

The following shows the relationship between the model, the

program modules which interact with the model, and the graph-

ical display and interaction system (e.g. OpenGL and GLUT).

Traversal for display

MODELdata structuresand procedures

Interactionswith model

modificationBuilding

manipulation

for analysisTraversal

Graphical"front end"

interactionand

interface

Display

191

Page 192: The full set of slides

With reference to the object hierarchy, we often refer to the

instantiation of an object or component of an object as call-

ing the object (analogous to calling a function in a procedural

hierarchy).

Again, analogously, we further refer to the passing of geometric

parameters in reference to the transformations applied when

the component is instantiated.

Some graphics packages include support for hierarchical mod-

eling. PHIGS, for example, supports a kind of database called

the central structure storage (CSS) which allows the storage

and retrieval of structures.

A structure is a sequence of elements — primitives, transfor-

mations, attributes, and other structures.

Generally, the CSS provides information directly to the dis-

play subsystem, and is itself modified and updated by special

functions. (In the previous diagram, the CSS is the MODEL,

generally optimized for display transversal.)

An example of a hierarchical structure contained in a CSS might

be a street of houses. The street structure (position, size, orien-

tation) would be the highest level, with the house structures at

the next level, and the particular house type at the next level.

192

Page 193: The full set of slides

The overall structure network might be diagrammed as follows:

STREET

rotate

HOUSEexecute

execute

execute

translateBUNGALOW

Here the term execute means apply an instance of the entity

to which the structure is pointing. This is called retained mode

graphics. We have used immediate mode graphics, where en-

tities to be displayed are described directly, not by reference.

A CSS database is not necessary, but it does provide certain

advantages. It can provide fast graphical updates, since it is

optimized for graphical traversal.

Graphical input is also facilitated because input pick operations

can readily be associated with components (by traversing the

structure hierarchy.)

If the database requires frequent updating, however, (particu-

larly if the structure hierarchy itself changes) then the database

update operations can become slow.

193

Page 194: The full set of slides

Display lists

OpenGL does not provide a CSS, but does support display lists.

A display list is a way to organize and name a set of OpenGL

commands. The commands may then be “executed” by “call-

ing” the display list.

Display lists are identified by an unsigned integer of type GLuint.

The function glGenlists(n) allocates n contiguous, previ-

ously unallocated display list indices. Zero is returned if the

request cannot be filled, or if the argument is 0.

glNewList(list,mode) specifies the start of the display list

with index list. The mode can be either GL_COMPILE or

GL_COMPILE_AND_EXECUTE. The latter both executes the com-

mands in immediate mode and places them in the display list.

glEndList() indicates the end of the current display list.

The following code fragment illustrates their use:

listIndex = glGenlists(1); /* generate 1 list index */

if (listIndex != 0) {

glNewList(listIndex, GL_COMPILE);

... /* OpenGL commands */

glEndList();

}

194

Page 195: The full set of slides

Executing a display list

Display lists are executed using the function glCallList(list)

where list is the index for the display list to be executed.

A call list can be executed several times. E.g., given a list with

label wheel it could be instantiated twice as follows:

glTranslatef(1.0, 0.0, 0.0);

glCallList(wheel);

glTranslatef(6.0, 0.0, 0.0);

glCallList(wheel);

Display lists can be hierarchical — one list can call another.

Suppose that we want to render a bicycle from component parts:

glNewList(listIndex, GL_COMPILE);

glTranslatef(1.0, 0.0, 0.0);

glCallList(frame);

glTranslatef(0.0, 0.0, 0.0);

glCallList(wheel);

glTranslatef(6.0, 0.0, 0.0);

glCallList(wheel);

glTranslatef(-1.0, 2.0, 0.0);

glCallList(handlebars);

glEndList();

195

Page 196: The full set of slides

What is stored in a display list?

A display list contains only the “current” values of expressions

stored in the list.

For example, if a vertex array is specified and the array ele-

ments are changed subsequent to the list being “compiled” the

new values are not reflected in the display list. Similarly, if a

transformation matrix is specified and subsequently modified,

the value at the time the list is compiled is used.

Some OpenGL commands cannot be stored in a display list.

Generally, things which change a state “out of scope” (e.g., a

global state change) are not allowed. glFlush() for example.

Also, the list commands themselves [glNewList(), glDeleteLists(),

glGenlists()] cannot be used inside the display list.

Other commands requiring feedback (glReadPixels(),

glFeedbackBuffer(), etc.) cannot be used because of the

client-server nature of OpenGL.

Also, commands which alter the state of the client such as

glEnableClientState(), glDisableClientState(), etc.

cannot be used, for the same reason.

The display list can accommodate hierarchy, and there is a limit

to the depth of the hierarchy; the minimum depth is 64.

196

Page 197: The full set of slides

What should be in a display list?

Display lists are created primarily to allow rapid rendering of

complex structures. The “compilation” process can be likened

to creating an image in a non-displayed area, and transforming

this image at instantiation.

The limitation of a display list is that the content is static —

the primitives internal to the list cannot be updated.

The following are guidelines only:

• Components which will be reused in a scene (e.g., the arms

of the robot)

• Components which will be redrawn frequently (e.g., the

cube — redrawn each spin angle change)

• organizing state changes that will be applied many times

The last item will be more useful later, when we discuss lighting

models and material parameters.

Recall that, if a state variable is set in a call list, (e.g., a color

is set with glColor*) the state change will persist after the

display list has been executed. The state variables can be saved

and returned using glPushAttrib() and glPopAttrib().

197

Page 198: The full set of slides

The argument for glPushAttrib() is from the following list:

Mask bit Attribute group

GL_ALL_ATTRIB_BITS all groups

GL_ACCUM_BUFFER_BIT accumulate buffer

GL_COLOR_BUFFER_BIT color buffer

GL_CURRENT_BIT current

GL_DEPTH_BUFFER_BIT depth buffer

GL_ENABLE_BIT enable

GL_EVAL_BIT eval

GL_FOG_BIT fog

GL_HINT_BIT hint

GL_LIGHTING_BIT lighting

GL_LINE_BIT line

GL_LIST_BIT list

GL_PIXEL_MODE_BIT pixel

GL_POINT_BIT point

GL_POLYGON_BIT polygon

GL_POLYGON_STIPPLE_BIT polygon stipple

GL_SCISSOR_BIT scissor

GL_STENSIL_BUFFER_BIT stencil buffer

GL_TEXTURE_BIT texture

GL_TRANSFORM_BIT transform

GL_VIEWPORT_BIT viewport

198

Page 199: The full set of slides

Each bit in the preceding bit masks corresponds to a number of

individual state variables. Exactly which ones are found in the

OpenGL documentation.

Similarly, the commands we have already used for modeling,

glPushMatrix() and glPopMatrix() are useful for localizing

the effect of translations and rotations to the commands in the

display list.

There are a few other interesting functions for use with display

lists:

glDeleteLists(GLuint list GLsize range) deletes range

display lists starting with list.

glIsList(GLuint list) returns GL_TRUE if list is already

in use, otherwise it returns GL_FALSE.

Multiple display lists can be executed with one call. Arguments

include a pointer to an array which contains the list numbers

as offsets from some base.

The base is set with glListBase(GLint base).

glCallLists(GLsizei n, GLenum type, GLvoid *lists)

executes ⁀n display lists computed by adding the current dis-

play list base (set by glListBase()) to the value in the array

pointed to by *lists. The parameter type is an enumerated

type indicating the type of data pointed to by *list.

199

Page 200: The full set of slides

Interaction with the image — selection and picking

Selection

Selection is one of the modes of operation for OpenGL. In selec-

tion, drawing information is returned to the application rather

the framebuffer. The screen remains fixed while OpenGL is in

selection mode.

In selection, the scene is first drawn into the framebuffer, then

selection mode is entered (and, typically, the viewing volume

changed) and the scene redrawn. While in selection mode, the

contents of the framebuffer remain unchanged. When selection

mode exits, a list of the primitives that intersect the viewing

volume is returned.

The list of primitives returned (the hit record) is an array of

integer identifiers corresponding to entries in the name stack.

The name stack is constructed by loading names (integers) onto

it as drawing commands are issued while in selection mode.

The returned list indicates, for example, which primitives might

have been selected on the screen by the user.

200

Page 201: The full set of slides

To use the selection mechanism, the following steps are per-

formed:

1. The array to be used for the returned hit records is specified

with glSelectBuffer().

2. Selection mode is entered by specifying GL_SELECT with

glRenderMode().

3. The name stack is initialized using glInitNames() and

glPushName().

4. The viewing volume to use for selection is defined. Since

this is generally different from the viewing volume originally

used to draw the scene, the current transformation state

should be saved with glPushMatrix() and restored with

glPopMatrix().

5. Drawing commands and commands to manipulate the name

stack are issued so that each primitive of interest has an ap-

propriate name assigned.

6. Exit selection mode and process the returned selection data

(the hit records).

201

Page 202: The full set of slides

void glSelectBuffer(GLsizei size, GLuint *buffer);

Specifies the array to be used for the returned selection data;

buffer is a pointer to an array of unsigned integers into which

the data is put, and size indicates the maximum number of

values that can be stored in the array. glSelectBuffer()

must be called before entering selection mode.

GLint glRenderMode(GLenum mode);

Controls whether the application is in rendering, selection, or

feedback mode. The mode argument can be one of GL_RENDER

(the default), GLSELECT, or GL_FEEDBACK. The program re-

mains in a given mode until glRenderMode() is called again

with a different argument. Before entering selection mode,

glSelectBuffer() must be called to specify the selection ar-

ray. The return value is the number of selection hits or the

number of values placed in the feedback array when either mode

is exited; a negative value means that the selection or feed-

back array has overflowed. GLRENDER_MODE can be used with

glGetlntegerv() to obtain the current mode.

202

Page 203: The full set of slides

Creating the Name Stack

The name stack is the basis for the selection information that

is returned. It is first initialized with glInitNames(), which

simply clears the stack, and then integer names are added to it

while issuing corresponding drawing commands.

glPushName(), and glPopName(), allow pushing and popping

of the stack; glLoadName() replaces the name on the top of

stack with its argument.

The following code shows simple name-stack manipulation:

glInitNames ();

glPushName(0);

glPushMatrix(); /* save the transformation matrix */

/* create your desired viewing volume here */

glLoadName(l);

drawFirstObject(); /* FirstObject has the name 1 */

glLoadName(2);

drawSecondObject(); /* SecondObject and ThirdObject */

drawThirdObject(); /* both have the same name - 2 */

glPopMatrix (); /* restore previous transformation */

203

Page 204: The full set of slides

Note: Multiple objects can share the same name if there is no

need to distinguish between them.

void glPushName(GLuint name)

Pushes name onto the name stack. Pushing beyond the stack

depth generates the GL_STACK_OVERFLOW error.

GL_NAME_STACK_DEPTH can be used with glGetIntegerv()

to obtain the depth of the name stack. The depth of the name

stack is guaranteed to be at least sixty-four names.

void glPopName(void)

Pops one name off the top of the name stack. Popping an empty

stack generates the error GL_STACK_UNDERFLOW.

void glLoadName(GLuint name);

Replaces the value on the top of the name stack with name. If

the stack is empty, (e.g., right after glInitNames() is called)

glLoadName() generates the error GL_INVALID_OPERATION.

glPushName() should be called at least once to put something

on the name stack before calling glLoadName().

Calls to glPushName(), glPopName(), and glLoadName()

are ignored when not in selection mode. This can simplify cod-

ing by using these calls in the drawing code, and then using

the same drawing code in both selection and normal rendering

modes.

204

Page 205: The full set of slides

The Hit Record

In selection mode, a primitive that intersects the viewing vol-

ume causes a selection hit. Whenever a name-stack manip-

ulation command is executed or glRenderMode() is called,

OpenGL writes a hit record into the selection array if there has

been a hit since the last time the stack was manipulated or

glRenderMode() was called. Objects sharing the same name

will not generate multiple hit records. Hit records are may not

be written into the array until glRenderMode() is called.

Note: In addition to primitives, valid coordinates produced by

glRasterPos() can cause a selection hit. Also, in the case of

polygons, no hit occurs if the polygon would have been culled.

Each hit record consists of four items, in order:

• The number of names on the name stack when the hit oc-

curred.

• Both the minimum and maximum window-coordinate z val-

ues of all vertices of the primitives that intersected the view-

ing volume since the last recorded hit. These two values,

lying in the range [0, 1], are each multiplied by 232 − 1 and

rounded to the nearest unsigned integer.

• The contents of the name stack at the time of the hit, with

the bottommost element first.

205

Page 206: The full set of slides

On entering selection mode, OpenGL initializes a pointer to the

beginning of the selection array. Each time a hit record is writ-

ten into the array, the pointer is updated accordingly. If writing

a hit record would cause the number of values in the array to

exceed the size argument specified with glSelectBuffer(),

OpenGL writes as much of the record as fits in the array and

sets an overflow flag. When selection mode is exited with

glRenderMode(), this command returns the number of hit

records written (including a partial record if there was one),

clears the name stack, and resets the stack pointer and overflow

flag. If the overflow flag had been set, the return value is -1.

Remember that the depth information is a scaled integer, rep-

resenting an unsigned number between 0 and 1. Dividing the

returned number by the largest unsigned integer will give the

actual depth in the window. To get the depth in world coor-

dinates, the transformation matrix parameters (e.g., the argu-

ments to glOrtho()) are required.

206

Page 207: The full set of slides

A Selection Example

In the sample program select.c, four triangles (green, red,

and two yellow triangles, created by calling drawTriangle())

and a wireframe box showing the viewing volume (drawViewVolume())

are drawn to the screen. Then the triangles are rendered again

(selectObjects()), in selection mode. The corresponding hit

records are processed in processHits(), where the hit list is

printed out. The first triangle generates a hit, the second does

not, and the third and fourth together generate a single hit.

Look at this code carefully to understand its operation.

/* draw a triangle with vertices at (x1, y1), (x2, y2)

* and (x3, y3) at z units away from the origin.

*/

void drawTriangle (GLfloat x1, GLfloat y1, GLfloat x2,

GLfloat y2, GLfloat x3, GLfloat y3, GLfloat z)

{

glBegin (GL_TRIANGLES);

glVertex3f (x1, y1, z);

glVertex3f (x2, y2, z);

glVertex3f (x3, y3, z);

glEnd ();

}

207

Page 208: The full set of slides

void draw4Triangles (void)

{

glLoadName(1); /* ignored in render mode */

glColor3f (0.0, 1.0, 0.0); /* green triangle */

drawTriangle (2.0, 2.0, 3.0, 2.0, 2.5, 3.0, -5.0);

glLoadName(2);

glColor3f (1.0, 0.0, 0.0); /* red triangle */

drawTriangle (2.0, 7.0, 3.0, 7.0, 2.5, 8.0, -5.0);

glLoadName(3);

glColor3f (1.0, 1.0, 0.0); /* yellow triangles */

drawTriangle (2.0, 2.0, 3.0, 2.0, 2.5, 3.0, 0.0);

drawTriangle (2.0, 2.0, 3.0, 2.0, 2.5, 3.0, -10.0);

}

208

Page 209: The full set of slides

void drawScene (void)

{

glMatrixMode (GL_PROJECTION);

glLoadIdentity ();

gluPerspective (40.0, 4.0/3.0, 1.0, 100.0);

glMatrixMode (GL_MODELVIEW);

glLoadIdentity ();

gluLookAt (7.5, 7.5, 12.5, 2.5, 2.5, -5.0,

0.0, 1.0, 0.0);

draw4Triangles();

drawViewVolume (0.0, 5.0, 0.0, 5.0, 0.0, 10.0);

}

drawViewVolume() draws a view volume which will be used as

the clipping volume in the next function, selectObjects().

#define BUFSIZE 512

209

Page 210: The full set of slides

void selectObjects(void)

{

GLuint selectBuf[BUFSIZE];

GLint hits;

glSelectBuffer (BUFSIZE, selectBuf);

(void) glRenderMode (GL_SELECT); /* set SELECT mode */

glInitNames(); /* initialize name stack */

glPushName(0);

glPushMatrix ();

glMatrixMode (GL_PROJECTION);

glLoadIdentity ();

glOrtho (0.0, 5.0, 0.0, 5.0, 0.0, 10.0);

glMatrixMode (GL_MODELVIEW);

glLoadIdentity ();

draw4Triangles();

glPopMatrix ();

glFlush ();

hits = glRenderMode (GL_RENDER); /* no. of hits */

processHits (hits, selectBuf);

}

210

Page 211: The full set of slides

void processHits (GLint hits, GLuint buffer[])

{

unsigned int i, j;

GLuint names, *ptr;

float z1, z2;

printf ("hits = %d\n", hits);

ptr = (GLuint *) buffer;

for (i = 0; i < hits; i++) { /* for each hit */

/* ptr points to hit list - returns no. of names,

min and max z coordinates, and each name in the

name stack, bottommost first */

names = *ptr; ptr++;

z1 = (float) *ptr/0x7fffffff; ptr++; /* scale 0-2 */

z2 = (float) *ptr/0x7fffffff; ptr++;

printf(" number of names for hit = %d\n", names);

printf(" z1 is %g; z2 is %g\n", z1, z2);

printf (" the name is ");

for (j = 0; j < names; j++) /* for each name */

printf ("%d ", *ptr); ptr++;

printf ("\n");

}

}

211

Page 212: The full set of slides

Note that the buffer containing the hit record was defined with

glSelectBuffer(BUFSIZE, selectBuf);

and contains data of type GLuint relating to the hit.

The code shown is “boiler plate” C code for obtaining values

from the hit record. Note that an array index could be used in

place of the pointer (ptr).

The output is basically the contents of the hit record, in the

order in which it was written. Note that in this example, there

is only one name for each item.

The code normalizes the z depth information in the range 0 – 2.

(Dividing the z values returned by the largest unsigned integer

(0xffffffff) would normalize to the range 0–1.)

Usually, the information in the hit record would be used to

trigger some other event; e.g., highlighting the items in the

view volume.

Selection can be used in a number of interesting applications;

for example, to model a “sense of touch” for an object.

212

Page 213: The full set of slides

Sample output for select.c

hits = 2

number of names for hit = 1

z1 is 1; z2 is 1

the name is 1

number of names for hit = 1

z1 is 0; z2 is 2

the name is 3

Recall that the green triangle had label 1, the red triangle had

label 2 and the two yellow triangles had label 3.

Clearly, the select operation found the green and yellow trian-

gles, but did not select the red triangle.

213

Page 214: The full set of slides

Picking

The picking operation is really a special case of selection. A

special picking matrix is used in conjunction with the projection

matrix to restrict drawing to a small region of the viewport,

typically near the cursor.

Some form of input, such as clicking a mouse button, initiates

selection (pick) mode. With selection mode set, together with

the special picking matrix, objects drawn near the cursor cause

selection hits.

Thus, during picking you are typically determining which ob-

jects are drawn near the cursor.

Picking is set up similarly to regular selection mode, with the

following major differences:

• Picking is usually triggered by an input device. In the

following code examples, pressing the left mouse button

invokes a function that performs picking.

• The utility routine gluPickMatrix() is used to multiply

a special picking matrix onto the current projection matrix.

214

Page 215: The full set of slides

gluPickMatrix() should be called before setting a standard

projection matrix (such as gluPerspective() or glOrtho()).

Normally, the contents of the projection matrix are first saved,

so the sequence of operations may look like this:

glMatrixMode (GL_PROJECTION);

glPushMatrix ();

glLoadIdentity ();

gluPickMatrix (...);

gluPerspective, glOrtho, glOrtho2D, or glFrustum

/* ... draw scene for picking */

/* ... perform picking ... */

glPopMatrix();

void gluPickMatrix(GLdouble x, GLdouble y, GLdouble

width, GLdouble height, GLint viewport[4]);

Creates a projection matrix that restricts drawing to a small

region of the viewport and multiplies that matrix onto the cur-

rent matrix stack. (x, y) is the center of the picking region in

window coordinates, typically the cursor location. width and

height define the size of the picking region in screen coordi-

nates. viewport[] indicates the current viewport boundaries,

obtained by calling

glGetIntegerv(GL_VIEWPORT, GLint *viewport);

215

Page 216: The full set of slides

The net result of the matrix created by gluPickMatrix() is

to transform the pick volume into the canonical clipping re-

gion −1 < (xyz) < 1 (or −w < (wx,wy, wz) < w). Since

the transformation is arbitrary, you can make picking work for

different sorts of regions—for example, for rotated rectangular

portions of the window.

In certain situations, you might find it easier to specify addi-

tional clipping planes to define the picking region.

The program pickdepth.c illustrates simple picking of over-

lapping elements.

In this example, when the left mouse button is pressed, the

function pickRects() is called to identify which rectangles

were picked by the mouse.

Note that for one press of the mouse key, several hits (corre-

sponding to the different rectangles under the cursor) may be

returned.

The individual rectangles can be distinguished by their depth

values.

This code should be examined carefully.

216

Page 217: The full set of slides

Sample code from pickdepth.c (slightly simplified)

void myinit(void)

{

glClearColor(0.0, 0.0, 0.0, 0.0);

glDepthFunc(GL_LESS);

glEnable(GL_DEPTH_TEST);

glShadeModel(GL_FLAT);

glDepthRange(0.0, 1.0); /* The default z mapping */

}

void

display(void)

{

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

drawRects();

glutSwapBuffers();

}

The function drawRects() draws 3 overlapping rectangles,

with colors yellow, cyan, and magenta named 1, 2, and 3, re-

spectively.

217

Page 218: The full set of slides

void drawRects()

/* slightly simplified from the original */

{

glLoadName(1);

glBegin(GL_QUADS);

glColor3f(1.0, 1.0, 0.0); /* yellow */

glVertex3i(2, 0, 0); glVertex3i(2, 6, 0);

glVertex3i(6, 6, 0); glVertex3i(6, 0, 0);

glEnd();

glLoadName(2);

glBegin(GL_QUADS);

glColor3f(0.0, 1.0, 1.0); /* cyan */

glVertex3i(3, 2, -1); glVertex3i(3, 7, -1);

glVertex3i(8, 7, -1); glVertex3i(8, 2, -1);

glEnd();

glLoadName(3);

glBegin(GL_QUADS);

glColor3f(1.0, 0.0, 1.0); /* magenta */

glVertex3i(0, 2, -2); glVertex3i(0, 8, -2);

glVertex3i(5, 8, -2); glVertex3i(5, 2, -2);

glEnd();

}

218

Page 219: The full set of slides

/* pickRects() is the mouse callback which sets up

* selection mode, name stack, and projection matrix

* for picking. Then the objects are drawn.

*/

#define BUFSIZE 512

void pickRects(int button, int state, int x, int y)

{

GLuint selectBuf[BUFSIZE];

GLint hits;

GLint viewport[4];

if (button != GLUT_LEFT_BUTTON || state != GLUT_DOWN)

return;

glGetIntegerv(GL_VIEWPORT, viewport);

glSelectBuffer(BUFSIZE, selectBuf);

(void) glRenderMode(GL_SELECT);

glInitNames();

glPushName(-1);

219

Page 220: The full set of slides

glMatrixMode(GL_PROJECTION);

glPushMatrix();

glLoadIdentity();

/* create 5x5 pixel pick region near cursor location */

gluPickMatrix((GLdouble) x, (GLdouble) (viewport[3]-y),

5.0, 5.0, viewport);

glOrtho(0.0, 8.0, 0.0, 8.0, -0.5, 2.5);

drawRects();

glPopMatrix();

glFlush();

hits = glRenderMode(GL_RENDER);

processHits(hits, selectBuf);

}

220

Page 221: The full set of slides

void processHits(GLint hits, GLuint buffer[])

/* this is the same "boiler plate" as before */

{

unsigned int i, j;

GLuint names, *ptr;

float z1, z2;

printf("hits = %d\n", hits);

ptr = (GLuint *) buffer;

for (i = 0; i < hits; i++) { /* for each hit */

names = *ptr; ptr++;

z1 = (float) *ptr/0xffffffff; ptr++; /* scale 0-1 */

z2 = (float) *ptr/0xffffffff; ptr++;

printf(" number of names for hit = %d\n", names);

printf(" z1 is %g; z2 is %g\n", z1, z2);

printf(" the name is ");

for (j = 0; j < names; j++) { /* for each name */

printf("%d ", *ptr); ptr++;

}

printf("\n");

}

}

221

Page 222: The full set of slides

sample output from pickdepth.c

For a cursor click in the center:

hits = 3

number of names for hit = 1

z1 is 0.166667; z2 is 0.166667

the name is 1

number of names for hit = 1

z1 is 0.5; z2 is 0.5

the name is 2

number of names for hit = 1

z1 is 0.833333; z2 is 0.833333

the name is 3

222

Page 223: The full set of slides

Using Multiple Names in a Hierarchical Model

Multiple names can also be used to distinguish between parts

of a hierarchical object in a scene.

For example, if you were rendering an assembly line of automo-

biles, you might want the ability to move the mouse to pick the

third bolt on the left front tire of the third car in line. Since the

bolts are identical, it is logical to have the same name for each

bolt, and to establish the particular bolt picked using a name

hierarchy. A different name can be used to identify each level

of hierarchy: which car, which tire, and finally which bolt.

As another example, one name could be used to describe a

single molecule among other molecules, and additional names

can differentiate individual atoms within that molecule.

The following example draws an automobile with four identical

wheels, each of which has five identical bolts. Code has been

added to manipulate the name stack with the object hierarchy.

223

Page 224: The full set of slides

/* Example showing the creation of multiple names */

draw_wheel_and_bolts()

{

long i;

draw_wheel_body();

for (i = 0; i < 5; i++) {

glPushMatrix();

glRotate(72.O*i, 0.0, 0.0, 1.0);

glTranslatef(3.0, 0.0, 0.0);

glPushName(i);

draw_bolt_body();

glPopName ();

glPopMatrix();

}

}

224

Page 225: The full set of slides

draw_body_and_wheel_and_bolts()

{

drawcarbody();

glPushMatrix();

glTranslate(4O, 0, 20); /* first wheel position */

glPushName(l); /* name of wheel number 1 */

draw_wheel_and_bolts();

glPopName ();

glPopMatrix();

glPushMatrix();

glTranslate(4O, 0, 20); /* second wheel position */

glPushName(2); /* name of wheel number 2 */

draw_wheel_and_bolts();

glPopName ();

glPopMatrix ();

/* draw last two wheels similarly */

}

The following example uses the previous routines in to draw

three different cars, numbered 1, 2, and 3:

225

Page 226: The full set of slides

draw_three_cars ()

{ glInitNames ();

glPushMatrix();

translate_to_first_car_position();

glPushName(l);

draw_body_and_wheel_and_bolts ();

glPopName();

glPopMatrix();

glPushMatrix();

translate_to_secondcar_position ();

glPushName(2);

draw_body_and_wheel_and_bolts ();

glPopName ();

glPopMatrix();

glPushMatrix ()

translate_to_third_car_position();

glPushName(3);

draw_body_and_wheel_and_bolts();

glPopName ();

glPopMatrix();

}

226

Page 227: The full set of slides

Assuming that picking is performed, the following are some

possible name-stack return values and their interpretations. In

these examples, at most one hit record is returned; also, d1 and

d2 are depth values.

2 d1 d2 2 1 Car 2, wheel 1

1 d1 d2 3 Car 3 body

3 d1 d2 1 1 0 Bolt 0 on wheel 1 on car I

empty The pick was outside all cars

The last interpretation assumes that the bolt and wheel don’t

occupy the same picking region. A user might well pick both

the wheel and the bolt, yielding two hits. If you receive multiple

hits, you have to decide which hit to process, perhaps by using

the depth values to determine which picked object is closest to

the viewpoint.

227

Page 228: The full set of slides

The program picksquare.c illustrates picking with a name

hierarchy.

It demonstrates how to use multiple names to identify different

components of a primitive, in this case the row and column

positions of a selected object.

A 3 × 3 grid of squares is drawn, with each square a different

color. The board[3][3] array maintains the current amount

of blue for each square. When the left mouse button is pressed,

the picksquare() routine is called to identify which squares

were picked by the mouse.

Two names (integers) identify each square in the grid; one iden-

tifies the row, and the other the column.

Also, in this example, when the left mouse button is pressed,

the color of all squares under the cursor position changes.

Again, this code should be examined carefully.

228

Page 229: The full set of slides

Sample code from picksquare.c

void drawSquares(GLenum mode)

{

GLuint i, j;

for (i = 0; i < 3; i++) {

if (mode == GL_SELECT)

glLoadName (i);

for (j = 0; j < 3; j ++) {

if (mode == GL_SELECT)

glPushName (j); /* note name hierarchy */

glColor3f ((GLfloat) i/3.0, (GLfloat) j/3.0,

(GLfloat) board[i][j]/3.0);

glRecti (i, j, i+1, j+1);

if (mode == GL_SELECT)

glPopName ();

}

}

}

229

Page 230: The full set of slides

/* pickSquares() sets up selection mode, name stack,

* and projection matrix for picking. Then the

* objects are drawn.

*/

#define BUFSIZE 512

void pickSquares(int button, int state, int x, int y)

{

GLuint selectBuf[BUFSIZE];

GLint hits;

GLint viewport[4];

if (button != GLUT_LEFT_BUTTON || state != GLUT_DOWN)

return;

glGetIntegerv (GL_VIEWPORT, viewport);

glSelectBuffer (BUFSIZE, selectBuf);

(void) glRenderMode (GL_SELECT);

glInitNames();

glPushName(0);

230

Page 231: The full set of slides

glMatrixMode (GL_PROJECTION);

glPushMatrix ();

glLoadIdentity ();

/* create 5x5 pixel pick region near cursor location */

gluPickMatrix ((GLdouble)x, (GLdouble)(viewport[3]-y),

5.0, 5.0, viewport);

gluOrtho2D (0.0, 3.0, 0.0, 3.0);

drawSquares (GL_SELECT);

glMatrixMode (GL_PROJECTION);

glPopMatrix ();

glFlush ();

hits = glRenderMode (GL_RENDER);

processHits (hits, selectBuf);

glutPostRedisplay();

}

231

Page 232: The full set of slides

void processHits (GLint hits, GLuint buffer[])

{

unsigned int i, j;

GLuint ii, jj, names, *ptr;

float z1, z2;

printf ("hits = %d\n", hits);

ptr = (GLuint *) buffer;

for (i = 0; i < hits; i++) { /* for each hit */

names = *ptr; ptr++;

z1 = (float) *ptr/0x7fffffff; ptr++;

z2 = (float) *ptr/0x7fffffff; ptr++;

printf (" number of names for hit = %d\n", names);

printf(" z1 is %g; z2 is %g\n", z1, z2);

printf (" names are ");

for (j = 0; j < names; j++) { /* for each name */

printf ("%d ", *ptr);

if (j == 0) ii = *ptr; /* set row and column */

else if (j == 1) jj = *ptr; ptr++;

}

printf ("\n");

board[ii][jj] = (board[ii][jj] + 1) % 3;

}

}

232

Page 233: The full set of slides

Sample output for picksquare.c

Output when selecting the lower left corner square:

hits = 1

number of names for this hit = 2

z1 is 1; z2 is 1

names are 0 0

Output when selecting the top center square:

hits = 1

number of names for this hit = 2

z1 is 1; z2 is 1

names are 1 2

233

Page 234: The full set of slides

Hints for Writing a Program That Uses Selection

Most programs that allow a user to interactively edit some ge-

ometry provide a mechanism for the user to pick items or groups

of items for editing. For two-dimensional drawing programs (for

example, text editors, circuit-layout programs, etc.), it might

be easier to do the picking calculations in the user code rather

than using the OpenGL pick mechanism. Often, it is easy to

find bounding boxes for two-dimensional objects and to organize

them in some hierarchical data structure to speed up searches.

For example, picking that uses the OpenGL style in a VLSI

layout program containing millions of rectangles can be quite

slow. However, with a good data structure, and using sim-

ple bounding-box information and other geometry constraints,

(e.g., only rectangular primitives aligned with the screen) could

make picking in such a program extremely fast. (The code may

also be simpler, as well.)

If you decide to use OpenGL picking, organize your program

and its data structures so that it is easy to draw appropriate lists

of objects in either selection or normal drawing mode. Then,

when the user picks something, the same data structures can

be used for the pick operation.

234

Page 235: The full set of slides

A possible interface design for a two-dimensional picking:

• All selection is done by pointing with the mouse cursor and

using the left mouse button.

• Clicking on an item selects it and deselects all other cur-

rently selected items. If the cursor is on top of multiple

items, the smallest is selected.

• Holding the button down while dragging the cursor, and

then releasing the button, selects all the items in a screen-

aligned rectangle with corners determined by the cursor

positions of the end points where the button changed state.

This is called a sweep selection.

• Holding the Shift key down when the user clicks on an item

not currently selected, adds that item to the select list. If

it was already selected, it is deleted.

• A sweep selection performed with the Shift key pressed adds

the items swept to the select list.

• If the user clicks on multiple (stacked) items, select just one

of them. If the cursor isn’t moved, and the user clicks again

in the same place, deselect the item originally selected, and

select a different item under the cursor. Use repeated clicks

at the same point to cycle through all the possibilities.

235

Page 236: The full set of slides

Of course, different situations require different approaches. For

example, in a text editor, you should not have to be concerned

with overlapping characters.

In three-dimensional editors, you might consider providing ways

to rotate and zoom between selections, or use the three ortho-

graphic projections, so complex methods for cycling through

the possible overlapping selections might be unnecessary.

In general, however, selection in three dimensions is difficult

because the position of the cursor on the screen usually gives

no indication of its depth.

The pick function has many other uses, as well.

Consider the case of a robot with “thumbs” used to pick up

an object in a 3-dimensional scene. How could we determine

whether the thumbs actually “contacted” the object?

One approach would be to construct a pick matrix selecting an

area about the thumbs, and look for “hits” of the thumb and

the object.

236

Page 237: The full set of slides

Other common interactions

Graphics editors generally have methods related to pick and

select for picking and selecting a whole area or a whole higher

level structure.

We have already seen briefly the use of sweep selection. This is

generally implemented with a rubberbanding style — the sweep

rectangle is drawn as an outline which follows the cursor until,

say, a mouse button is released. (In sweep selection, normally

the rectangle disappears.)

This is a useful idea for other applications. For example, it is

often used to let the user set the size of various simple graphics

primitives (e.g., rectangles, circles, ellipses, polygons in pro-

grams like xfig.)

Another common, useful, facility is to place handles on com-

plex components. These handles are points at which the whole

component can be selected so that some operation could be per-

formed on it (repositioning is the most typical, as is resizing or

deletion — xfig uses handles for these.)

Often programs using such constructs have several modes; say,

drawing and editing. The handles may only be displayed in

editing mode.

237

Page 238: The full set of slides

Modelling curves and curved surfaces

So far, we have been largely satisfied with surfaces constructed

from polygonal patches. In fact, we have used models mostly

composed of rectangular or triangular solids.

Of course, OpenGL only renders simple polygonal surfaces, so

any other surfaces or lines must eventually be modelled using

those primitives.

We have briefly looked at some of the glu and glut functions

for a cylinder and a cube. In fact, the glu library has func-

tions for a number of interesting curves and surfaces. A set of

functions for quadric surfaces is available.

A quadric surface is defined by the general quadratic equation

in 3 dimensions:

a1x2+a2y

2+a3z2+a4xy+a5yz+a6xz+a7x+a8y+a9z+a10 = 0

The quadric surfaces available in glu are not the fully general

quadric surfaces, but include the most commonly used func-

tions.

238

Page 239: The full set of slides

The following steps are required to use a quadrics object:

1. Call gluNewQuadric() to create a quadrics object.

2. Specify the rendering attributes for the quadrics object (or

use the default values).

(a) Use gluQuadricOrientation() to differentiate the

interior from the exterior.

(b) Use gluQuadricDrawStyle() to determine whether

the object is rendered as points, lines, or filled polygons.

(c) Use gluQuadricNormals() to specify one normal per

vertex or one normal per face, if normals are required.

(The default is that no normals are generated.)

3. Define error-handling routines with gluQuadricCallback().

Then, if an error occurs during rendering, the specified rou-

tine is invoked.

4. Invoke the rendering routine for the desired type of quadrics

object: gluSphere(), gluCylinder(), gluDisk(), or

gluPartialDisk(). For static data, encapsulate the quadrics

object in a display list for faster rendering.

5. When completely finished with the quadric, delete it with

gluDeleteQuadric(). If another quadric is needed, it is

better to reuse the quadrics object.

239

Page 240: The full set of slides

Managing Quadrics Objects

A quadrics object consists of parameters, attributes, and call-

backs that are stored in a data structure of type GLUquadricObj.

A quadrics object may generate vertices, normals, texture coor-

dinates, and other data, all of which may be used immediately

or stored in a display list. The following routines create, destroy,

and report errors in a quadrics object:

GLUquadricObj* gluNewQuadric(void);

Creates a new quadrics object and returns a pointer to it. A

null pointer is returned if the routine fails.

void gluDeleteQuadric(GLUquadricObj *qobj);

Destroys the quadrics object qobj and frees up any memory

used by it.

void gluQuadricCallback(GLUquadricObj *qobj,

GLenum w, void (*fn)());

Defines a function fn() to be called when an error occurs.

(GLU_ERROR is the only legal value for w.) If fn() is NULL,

any existing callback is erased.

For GLU_ERROR, fn() is called with one parameter, the error

code. gluErrorString() can be used to convert the error

code into an ASCII string.

240

Page 241: The full set of slides

Controlling Quadrics Attributes

The following routines affect the kinds of data generated by the

quadrics routines. They must be called before the primitives

are specified.

gluQuadricDrawStyle(GLUquadricObj *qobj,

GLenum drawStyle)

drawStyle controls the rendering style for qobj.

Possible values for drawStyle are GLU_POINT, GLU_LINE,

GLU_SILHOUETTE, and GLU_FILL.

GLU_POINT and GLU_LINE specify that primitives be rendered

as a point at every vertex or a line between each pair of con-

nected vertices.

GLU_SILHOUETTE specifies that primitives are rendered as lines,

but edges separating coplanar faces are not drawn. (This is most

often used with gluDisk() and gluPartialDisk().)

GLU_FILL specifies rendering by filled polygons, where the poly-

gons are drawn in a counterclockwise fashion with respect to

their normals.

This may be altered with gluQuadricOrientation().

241

Page 242: The full set of slides

void gluQuadricOrientation(GLUquadricObj *qobj,

GLenum orientation);

For the quadrics object qobj, orientation is either GLU_OUTSIDE

(the default) or GLU_lNSIDE, which controls the direction in

which normals are pointing.

For gluSphere() and gluCylinder(), the definitions of out-

side and inside are obvious. For gluDisk() and gluPartialDisk(),

the positive z side of the disk is considered to be outside.

void gluQuadricNormals(GLUquadricObj *qobj,

GLenum normals);

For the quadrics object qobj, normals is GLU_NONE (the de-

fault), GLU_FLAT, or GLU_SMOOTH.

gluQuadricNormals() is used to specify when to generate

normal vectors. GLU_NONE means that no normals are gener-

ated and is intended for use without lighting.

GLU_FLAT generates one normal for each facet; generally best

for lighting with flat shading.

GLU_SMOOTH generates one normal for every vertex of the quadric;

usually best for lighting with smooth shading.

242

Page 243: The full set of slides

Quadrics Primitives

The following routines actually generate the vertices and other

data that constitute a quadrics object. In each case, qobj refers

to a quadrics object created by gluNewQuadric().

void gluSphere(GLUquadricObj *qobj, GLdouble radius,

GLint slices, GLint stacks);

Draws a sphere of the given radius, centered around the origin,

(0, 0, 0). The sphere is subdivided around the z-axis into a

number of slices (similar to longitude) and along the z-axis

into a number of stacks (latitude).

The following shows a sphere with 15 slices and 10 stacks, drawn

in GLU_FILL mode with GLU_SMOOTH shading:

243

Page 244: The full set of slides

void gluCylinder(GLUquadricObj *qobj,

GLdouble baseRadius, GLdouble topRadius,

GLdouble height, GLint slices, GLint stacks);

Draws a cylinder oriented along the z-axis, with the base of the

cylinder at z = 0 and the top at z = height. The cylinder is

also subdivided into a number of slices around the z-axis and

number of stacks along the z-axis. baseRadius is the radius of

the cylinder at z = 0. topRadius is the radius of the cylinder at

z = height. If topRadius is set to 0, then a cone is generated.

Note: The cylinder is not closed at the top or bottom — no

disks are drawn at the base or the top.

The following shows a cylinder with 15 slices and 5 stacks, drawn

in GLU_FILL mode with GLU_FLAT shading:

244

Page 245: The full set of slides

void gluDisk(GLUquadricObj *qobj, GLdouble innerRadius,

GLdouble outerRadius, GLint slices, GLint rings);

Draws a disk on the z = 0 plane, with a radius of outerRadius

and a concentric circular hole with a radius of innerRadius.

If innerRadius is 0, then no hole is created. The disk is sub-

divided around the z-axis into a number of slices (like slices of

pie) and also about the z-axis into a number of concentric rings.

With respect to orientation, the +z side of the disk is considered

to be “outside;” that is, any normals generated point along the

+z axis. Otherwise, the normals point along the −z axis.

The following shows a disk with 20 slices and 4 rings, drawn in

GLU_LINE mode without shading (GLU_NONE):

245

Page 246: The full set of slides

void gluPartialDisk(GLUquadricObj *qobj, GLdouble

innerRadius, GLdouble outerRadius, GLint slices, GLint

rings, GLdouble startAngle, GLdouble sweepAngle);

The difference between a partial disk and a disk is that only a

portion of a partial disk is drawn, starting from startAngle

through startAngle + sweepAngle (where startAngle and

sweepAngle are measured in degrees; 0◦ is along the +y axis,

90◦ degrees is along the +x axis, 180◦ is along the −y axis, and

270◦ is along the −x axis).

The following shows a partial disk with 20 slices and 4 rings,

drawn in GLU_SILHOUETTE mode without shading (GLU_NONE)

drawn from 0◦ to 225◦:

246

Page 247: The full set of slides

The code for each of those quadric figures was of the form:

GLUquadricObj *qobj;

.

.

qobj = gluNewQuadric();

.

.

gluQuadricDrawStyle(qobj, GLU_FILL); /* solid figure */

gluQuadricNormals(qobj, GLU_FLAT); /* flat shaded */

glNewList(startlist, GL_COMPILE);

gluCylinder(qobj, 0.5, 0.3, 1.0, 15, 5);

glEndList();

Note: For all quadrics objects, it’s better to use the *Radius,

height, and similar arguments to scale the object rather than

the glScale*() command, so that the unit-length normals

that are generated do not have to be renormalized.

Setting the rings and stacks arguments to values other than

1 forces lighting calculations to be done at a finer granularity,

especially if the material specularity is high.

In this example, all the quadrics used the same object, qobj.

The program quadric.c shows code which draws these figures.

247

Page 248: The full set of slides

Other curved objects

For objects without the regularity of quadrics, the shape has to

be represented with other functions. For example, consider a

simple curve like the following, which was drawn freely.

It can be approximated to any degree of accuracy we like using

straight line segments. Of course, for very high accuracy, we

need very many straight line segments. This is an example of a

piecewise linear approximation to the curve.

It is also possible to make either global or local polynomial ap-

proximations to a curve. In fact, given n points, it is possible

to exactly fit an n − 1 order polynomial. If the points are not

exact (say, with measurement uncertainties) then an approxi-

mate polynomial of lower order may be fitted, using a number

of techniques.

248

Page 249: The full set of slides

Of course, OpenGL must eventually construct a curve from line

segments, but a curve or curved surface may be a more natural

representation from a modelling point of view.

Polynomial approximations to curves are particularly useful,

because they are relatively easy to determine. There are two

possibilities:

• A single polynomial which approximates the full curve by

interpolating or approximating all the points

• A set of polynomials each interpolating or approximating a

number of the points, in a piecewise manner

The previous figure showed a linear piecewise approximation to

the function.

In general, if the number of points is even moderately large, a

single polynomial will be of a high order, and introduce spurious

“wiggles” in the approximating function.

Using a piecewise polynomial approximation, lower order poly-

nomials can be used, and the curve may looks “smoother.”

249

Page 250: The full set of slides

The following shows an example of a set of points fitted with

a high order polynomial and a piecewise linear polynomial of

order 2:

2nd order polynomials, piecewise6th order polynomial, continuous

The classical way for a draftsman to draw a smooth curve was

to use a “mechanical spline” — a thin piece of flexible material

which was anchored to the drawing at a set of points called

knots.

The flexibility of the spline means that the curve will be smooth,

and the changes in curvature will be gradual.

There is a mathematical analogue to this process, which also

produces a low order piecewise polynomial approximation to a

set of points. The most commonly used polynomial is a cubic

polynomial, and it is the cubic spline which best matches the

mechanical spline.

250

Page 251: The full set of slides

The cubic spline

The following describes how we may find a function called cu-

bic spline that interpolates a given set of data.

The mathematical model for this can be derived as follows. Let

(xi, fi) with f(xi) ≡ fi, i = 1 : n be distinct pairs of points

with a = x1 < x2 < · · · < xn = b.

The cubic spline that interpolates these points is a function s(x)

defined on [a, b] with the following properties:

1. s(xi) = fi, i = 1 : n, (The spline passes through the

knots).

2. s(x), s′(x), s′′(x) are continuous on [a, b] , (The spline is

continuous, as are its first and second derivatives. There-

fore, it has no sharp angles.)

3. In each subinterval [xi, xi+1], i = 1 : n− 1 s(x) is a cubic

polynomial (but s(x) may be a different cubic polynomial

in each subinterval.)

4. The spline s(x) minimizes its potential energy. Since the

potential energy is approximately proportional to the sec-

ond derivative. then, if g(x) is any other function that

satisfies (1), (2), (3), then

∫ b

as′′(x)2dx ≤

∫ b

ag′′(x)2dx.

251

Page 252: The full set of slides

Property (4) is difficult to model, but it can be shown that

it holds provided that

s′′(a) = s′′(b) = 0, which will be used instead of (4).

It can be shown that there is precisely one function s(x) satis-

fying those conditions.

Computing cubic splines

Since s(x) is a cubic polynomial for x ∈ [xi, xi+1] then s′′(x)

must be linear, so:

s′′(x) = s′′(xi)x− xi+1

xi − xi+1

+ s′′(xi+1)x− xi

xi+1 − xi

= s′′(xi)x− xi + xi − xi+1

xi − xi+1

+ s′′(xi+1)x− xi

xi+1 − xi⇐⇒

s′′(x) = s′′(xi) +x− xi

xi+1 − xi[s′′(xi+1)− s′′(xi)] , (1)

To find s(x), x ∈ [xi, xi+1] this can be integrated twice; that is:

∫ x

xis′′(t)dt = [s′(t)]xxi = s′(x)− s′(xi). (2)

252

Page 253: The full set of slides

∫ x

xi

s′′(xi) +

t− xixi+1 − xi

[s′′(xi+1)− s′′(xi)]

dt

= s′′(xi)∫ x

xidt +

s′′(xi+1)− s′′(xi)

xi+1 − xi

∫ x

xi(t− xi)dt

= s′′(xi)(x− xi) +s′′(xi+1)− s′′(xi)

2(xi+1 − xi)(x− xi)

2. (3)

Relations (2) and (3) imply

s′(x) = s′(xi)+s′′(xi)(x−xi)+s′′(xi+1)− s′′(xi)

2(xi+1 − xi)(x−xi)

2 (4)

Integrating (4) gives

s(x) = s(xi) + s′(xi)(x− xi) +1

2s′′(xi)(x− xi)

2+

1

6

s′′(xi+1)− s′′(xi)

xi+1 − xi(x−xi)

3, x ∈ [xi, xi+1], i = 1 : n−1.

(5)

Denoting s′′i ≡ s′′(xi), and hi ≡ xi+1−xi and setting x ≡ xi+1

in (5) and solving for s′(xi) we get

fi+1 = fi + s′(xi)hi +1

2s′′i h

2i +

s′′i+1 − s′′i6hi

h3i ⇐⇒

s′(xi) =fi+1 − fi

hi− s′′i+1

hi

6− s′′i

hi

3(6)

253

Page 254: The full set of slides

From (5) and (6) it is obvious that if s′′i is known for i = 1 : n

then s(x) can be evaluated for any value of x ∈ [a, b].

The aim therefore is to find a way for the computation of s′′i ,

i = 1 : n. This can be done using (4); setting i ≡ i − 1 and

x ≡ xi obtains

s′(xi) = s′(xi−1) +1

2(s′′i + s′′i−1)hi−1. (7)

Relations (6) and (7) imply

s′(xi−1) +1

2(s′′i + s′′i−1)hi−1 =

fi+1 − fihi

− s′′i+1

hi

6− s′′i

hi

3. (8)

In order to eliminate s′(xi−1) from (8) set i ≡ i− 1 in (6) and

substitute the resulting expression for s′(xi−1) into (8).

After simplification,

hi−1s′′i−1+2(hi−1+hi)s

′′i+his

′′i+1 = 6

fi+1 − fi

hi− fi − fi−1

hi−1

︸ ︷︷ ︸

di

, i = 2 : n−1 .

(9)

A system of n − 2 linear equations can be constructed from

(9) to solve for the unknowns s′′i , i = 2 : n − 1. Note that

s′′1 = s′′n = 0. The system is as follows:

254

Page 255: The full set of slides

2(h1 + h2) h2

. . . . . . . . .

hn−3 2(hn−3 + hn−2) hn−2

hn−2 2(hn−2 + hn−1)

︸ ︷︷ ︸

H

s′′2...

s′′n−2

s′′n−1

︸ ︷︷ ︸

x

=

d2...

dn−2

dn−1

︸ ︷︷ ︸

d

H is symmetric tridiagonal and diagonally dominant. Using

Gaussian elimination to solve this system, we get

a2 h2

a3 h3

. . . . . .

an−2 hn−2

an−1

︸ ︷︷ ︸

A

s′′2s′′3...

s′′n−2

s′′n−1

︸ ︷︷ ︸

x

=

b2

b3...

bn−2

bn−1

︸ ︷︷ ︸

b

, (10)

where

a2 = 2(h1 + h2), ai = 2(hi−1 + hi)−h2i−1

ai−1

;

255

Page 256: The full set of slides

b2 = d2, bi = di −hi−1bi−1

ai−1

; for i = 3 : n− 1 .

Equation (10) can be solved by the recursion

s′′n−1 =bn−1

an−1

, s′′i =bi − his

′′i+1

ai, i = n− 2 : −1 : 2 .

Having computed s′′i for i = 2 : n− 2, s′i for i = 1 : n− 1 may

now be computed from (6).

Once this information is available s(x) may be computed for

any x ∈ [a, b] from (5).

This process may seem involved, but it is really the solution of

a set of linear equations.

The cubic spline is not often used for graphical applications,

even though it has the desirable properties of continuity in the

function values and the first and second derivatives.

For graphics, the major drawback of the cubic spline is the fact

that it is non-local — a change in the position of any knot affects

the whole curve.

In many graphical applications, the ability to distort a curve

locally but leaving far points unchanged is important.

256

Page 257: The full set of slides

Parametric cubic curves

Cubic polynomials are frequently used for piecewise interpola-

tion of sets of points because they are the lowest order polyno-

mials which can guarantee continuity of both the function and

the first derivative when interpolating two points.

It is possible to represent polynomial functions in parametric

form [x = Px(t), y = Py(t), z = Pz(t)], rather than in the more

familiar implicit form [P (x) = 0].

A parametric cubic polynomial curve segment

Q(t) = [x(t) y(t) z(t)], (0 ≤ t ≤ 1) has the following form:

x(t) = axt3 + bxt

2 + cxt + dx

y(t) = ayt3 + byt

2 + cyt + dy

z(t) = azt3 + bzt

2 + czt + dz

=[

t3 t2 t 1]

·

ax ay az

bx by bz

cx cy cz

dx dy dz

Defining T as the vector T = [t3 t2 t 1], the parametric equa-

tions can be rewritten as:

Q(t) = [x(t) y(t) z(t)] = T · Cwhere C is the parametric coefficient matrix.

257

Page 258: The full set of slides

Without loss of generality, the parameter t can be restricted to

the interval [0, 1].

In this form, it is easy to calculate the derivatives of Q(t). For

the first derivative,

ddtQ(t) = Q′(t) = d

dt[T · C] = [3t2 2t 1 0] · C

=[

3axt2 + 2bxt + cx 3ayt

2 + 2byt + cy 3azt2 + 2bzt + cz

]

If two curve segments joined at a point (knot) have the same

value for their first derivatives, they are said to be C1 continu-

ous, (If their first n derivatives have the same value at a knot,

the two curve segments are Cn continuous.)

If two curve segments joined at a point (knot) have first deriva-

tives with the same direction, but different magnitudes, they

are said to be G1 continuous; Cn ⇒ Gn, but not conversely.

Although G1 continuity is not as strong a constraint as C1 con-

tinuity, both types of constraints produce curves which appear

smooth. (The G refers to “geometry.”)

258

Page 259: The full set of slides

Constraints

A curve segment Q(t) is defined by constraints on such param-

eters as the end points, tangent vectors, continuity, at knots,

etc.

Cubic polynomials have four coefficients, so four constraints are

required. The constraints can be made explicit by rewriting the

coefficient matrix C as C = M · G where G is a 4-element

matrix of geometric constraints called the geometry matrix.

G and M are constant matrices, and

Q(t) =[

x(t) y(t) z(t)]

= T ·M ·G

=[

t3 t2 t 1]

·

m11 m12 m13 m14

m21 m22 m23 m24

m31 m32 m33 m24

m41 m42 m43 m44

·

G1

G2

G3

G4

Looking at only the x component, x(t) = Gx ·M · T ,x(t) = (t3m11 + t2m21 + tm31 +m41)g1x +

(t3m12 + t2m22 + tm32 +m42)g2x +

(t3m13 + t2m23 + tm33 +m43)g3x +

(t3m14 + t2m24 + tm34 +m44)g4x

The y and z components are obtained similarly.

259

Page 260: The full set of slides

This is really a weighted sum of the components of the geometry

matrix, where the weights (called blending functions, B =

T ·M) are cubic polynomials.

This is similar to the piecewise linear approximation (with linear

blending functions) where

x(t) = (1− t)g1x + tg2x

y(t) = (1− t)g1y + tg2y

z(t) = (1− t)g1z + tg2z

Here the blending functions are linear, but there are many pos-

sible generalizations.

The matrices G and M specify the geometric constraints and

the blending functions, B = T ·M , (also called basis polyno-

mials) respectively. There are many possible forms for the basis

polynomials.

In the following, we will see how to determine the basis matrix,

M , for various parametric cubic curves.

260

Page 261: The full set of slides

Hermite curves

The first parametric cubic curve we will look at is the Hermite

curve.

The Hermite form of the cubic polynomial is specified by the

two endpoints (P1 and P4) and their tangents (R1 and R4).

To find the Hermite basis matrixMH , we can solve the 4 equa-

tions relating the four constraints to the polynomial coefficients.

For the x component only, the Hermite geometry vector is

GHx = [P1x P4x R1x R4x ]T and

x(t) = axt3 + bxt

2 + cxt + dx = T · MH · GHx· =[

t3 t2 t 1] ·

MH ·GHx

so, substituting for the constraints (t = 0 and t = 1) directly,

x(0) = P1x = [0 0 0 1] ·MH ·GHx

x(1) = P4x = [1 1 1 1] ·MH ·GHx

Differentiating the expression for x(t) gives

x′(t) =[

3t2 2t 1 0]

·MH ·GHx

and substituting for t

x′(0) = R1x = [0 0 1 0] ·MH ·GHx

x′(1) = R4x = [3 2 1 0] ·MH ·GHx

Similar results are obvious for the y and z components.

261

Page 262: The full set of slides

Combining the four constraints in matrix form,

P1

P4

R1

R4

x

= GHx =

0 0 0 1

1 1 1 1

0 0 1 0

3 2 1 0

·MH ·GHx

For this to be true, MH must be the inverse of the constraint

matrix, so

MH =

0 0 0 1

1 1 1 1

0 0 1 0

3 2 1 0

−1

=

2 −2 1 1

−3 3 −2 −1

0 0 1 0

1 0 0 0

Recalling that the blending function is B = T ·M , and

Q(t) = T ·M ·G, the Hermite blending functions BH can be

calculated as the weights of the geometry elements

Q(t) = BH ·GH = T ·MH ·GH

= (2t3 − 3t2 + 1)P1 + (−2t3 + 3t2)P4 +

(t3 − 2t2 + t)R1 + (t3 − t2)R4

Since the Hermite curves are linear combinations of the elements

of the geometry vector, they can be transformed by applying

the blending functions to the transformed geometry vector.

Therefore, the Hermite polynomials are invariant under transla-

tion, rotation, and scaling (but not perspective transformation.)

262

Page 263: The full set of slides

Following is a plot of the Hermite blending functions which are

multiplied by each of the geometry elements:

HP1 HP4

HR1

HR4

Hermite blending functions

-0.2

0

0.2

0.4

0.6

0.8

1

0.40.20 0.6 0.8 1

Note that the weighting functions have a certain symmetry.

Also, the weighting for one of the derivatives (R4) is negative.

263

Page 264: The full set of slides

Bezier curves

The Hermite form of the cubic polynomial requires the deriva-

tives at the endpoints. The Bezier form specifies the endpoint

vectors using two intermediate points (P2 and P3) which do not

lie on the curve.

The end point tangent vectors are defined by

Q′(0) = 3(P2 − P1) = R1 and Q′(1) = 3(P4 − P3) = R4.

The Bezier geometry matrix GB is GB = [P1 P2 P3 P4]T

This can be written as a Hermite geometry matrix as follows:

GH =

P1

P4

R1

R4

=

1 0 0 0

0 0 0 1

−3 3 0 0

0 0 −3 3

·

P1

P2

P3

P4

= MHB · GB

To find the Bezier basis matrix, MB, from the Hermite form,

Q(t) = T ·MH ·GH = T ·MH · (MHB ·GB)

= T · (MH ·MHB) ·GB = T ·MB ·GB

So, MB = MH ·MHB =

−1 3 −3 1

3 −6 3 0

−3 3 0 0

1 0 0 0

264

Page 265: The full set of slides

Q(t) = T ·MB ·GB is

Q(t) = (1− t)3P1 + 3t(1− t)2P2 + 3t2(1− t)P3 + t3P4

The four blending polynomials BB = T · MB are called the

Bernstein polynomials.

0.2

0.4

0.6

0.8

1

0.4 0.6 10

B

B BB

2

1

34

0.2 0.8

Cubic Bernstein polynomials

They have the property that they are non-negative and sum to

1 at any point in [0, 1], so Q(t) is really a weighted average of

the control points. This has the consequence that the curve is

bounded by the four control points; the control points form a

convex hull for the curve segment. (This is useful for clipping

the curves.)

265

Page 266: The full set of slides

Joining curve segments

Both the Hermite and Bezier curves can be joined at a knot.

C1 or G1 continuity can be assured only if made a specific

constraint. For Hermite polynomials, the first derivatives at

the knot (R4 and R5) must be equal for C1 continuity, and

have the same direction for G1 continuity.

P

P

P

R

1

14

7

R4

R

7

R5

For Bezier curves, for G1 continuity, points P3, P4, and P5 must

be collinear. For C1 continuity, P4 − P3 must equal P5 − P4.

P

P

P1

4

7

P

P

P

2

3

5P6

Higher order Hermite and Bezier curves can be derived in a

similar manner, but are rarely used.

266

Page 267: The full set of slides

Cubic B-splines

These are curve segments which approximate a series of m+ 1

control points P0, P1, . . . , Pm where m ≥ 3 by a set of m − 2

cubic polynomial segments Q3, Q4, . . . , Qm.

For each i > 3 there is a knot between the value Qi−1 and Qi

at parameter value ti. Including the endpoints at parameters

t3 and tm+1, there are m− 1 knots.

If the spacing of the knots is uniform (equal separations in t),

then the spline is said to be a uniform B-spline.

Each curve segment is defined by four of the control points;

Qi(t), ti ≤ t < ti+1 is defined by Pi−3, Pi−2, Pi−1 and Pi.

P0

P7

t3

t4

t5 t6

t7t8

t9

t10

P6P5

P2

P1 P3

P8

P9

P4

Q3

Q4

Q5

Q6

Q7

Q8

Q9

267

Page 268: The full set of slides

The B-spline geometry matrix for segment Qi is

GBsi =[

Pi−3 Pi−2 Pi−1 Pi

]T

Defining Ti =[

(t− ti)3 (t− ti)

2 (t− ti) 1]

then Qi(t) = Ti ·MBs ·GBsi, ti ≤ t < ti+1

The B-spline basis matrix MBs is the following:

MBs = 1/6

−1 3 −3 1

3 −6 3 0

−3 0 3 0

1 4 1 0

The B-spline blending functions BBs are obtained as Ti ·MBs.

This gives BBs =[

BBs−3BBs−2

BBs−1BBs0

]

where

BBs−3= 1/6(−(t− ti)

3 + 3(t− ti)2 − 3(t− ti) + 1)

simplifying,

BBs−3= 1/6(1− (t− ti)

3)

Similarly,

BBs−2= 1/6(3(t− ti)

3 − 6(t− ti)2 + 4)

BBs−1= 1/6(−3(t− ti)

3 + 3(t− ti)2 + 3(t− ti) + 1)

BBs0 = 1/6(t− ti)3

268

Page 269: The full set of slides

Replacing t− ti by t and replacing the interval [ti, ti+1] by [0, 1]

BBs =[

BBs−3BBs−2

BBs−1BBs0

]

=1

6

[

(1− t)3 (3t3 − 6t2 + 4) (−3t3 + 3t2 + 3t + 1) t3]

, 0 ≤ t < 1

03

2 1

1/6

2/6

3/6

4/6

5/6

10.2 0.4 0.6 0.80

B B

B

Bs-

B

Bs-

B-spline blending functions

Bs-Bs-

The uniform B-spline is C2 continuous at the knots.

Note that the blending functions are non-negative and sum to

1, so B-splines also have the convex hull property.

Because three functions are non-zero at the endpoints, it does

not interpolate any of its control points, unless the points are

replicated. Then, however, the order of the polynomial segment

is reduced.

269

Page 270: The full set of slides

Non-uniform B-Splines

A non-uniform spline has knot values which are not uniformly

distributed in the parameter t. In this case, the blending func-

tions are not fixed, but vary from curve segment to segment.

For the non-uniform cubic B-spline, with control points P0 to

Pm, the knot value sequence is a non-decreasing sequence of

values t0 to tm+4. (Note that there are 4 more knots than control

points.)

The fact that the interval is non-decreasing allows successive

knots to be equal; knots may have a multiplicity > 1. This

implies that there may be a curve segment which reduces to a

point.

A curve segmentQi is defined by control points Pi−3, Pi−2,Pi−1,Pi

and by blending functions Bi−3,4(t),Bi−2,4(t),Bi−1,4(t),Bi,4(t),

as the weighted sum

Qi(t) = Pi−3Bi−3,4(t)+Pi−2Bi−2,4(t)+Pi−1Bi−1,4(t)+PiBi,4(t)

(the subscript 4 refers to the order of the curve, and is 1 plus

the degree — 4 for a cubic curve.)

The blending functions are defined recursively in terms of the

lower order blending functions.

270

Page 271: The full set of slides

In general, the blending functions Bi,k(t) are defined as follows:

Bi,1(t) =

1, ti ≤ t < ti+1

0, otherwise

and if k > 1, and tk−1 ≤ t < tn+1

Bi,k(t) =t− ti

ti+k−1 − tiBi,k−1(t) +

ti+k − t

ti+k − ti+1

Bi+1,k−1(t)

For a cubic B-spline, the recurrence follows:

Bi,1(t) =

1, ti ≤ t < ti+1

0, otherwise

Bi,2(t) =t− ti

ti+1 − tiBi,1(t) +

ti+2 − t

ti+2 − ti+1

Bi+1,1(t),

Bi,3(t) =t− ti

ti+2 − tiBi,2(t) +

ti+3 − t

ti+3 − ti+1

Bi+1,2(t),

Bi,4(t) =t− ti

ti+3 − tiBi,3(t) +

ti+4 − t

ti+4 − ti+1

Bi+1,3(t).

Again, the blending functions are non-negative and sum to 1 so

the convex hull property holds.

It is possible for the denominators above to be 0 if the multi-

plicity of a knot is > 1; in this case the value is defined to be

0, (i.e., division by 0 is defined to yield 0.)

271

Page 272: The full set of slides

Non-uniform rational B-splines (NURBS)

It is possible to define all of the curves seen so far using ra-

tional polynomials. They can be thought of as polynomials in

homogeneous coordinates:

x(t) =X(t)

W (t), y(t) =

Y (t)

W (t), z(t) =

X(t)

W (t)

Here, X(t), Y (t), and Z(t) are polynomials with control points

defined in homogeneous coordinates.

Any non-rational polynomial can be transformed to a rational

polynomial by setting W (t) = 1.

NURBS, being defined in a homogeneous coordinate system, are

invariant under rotation, translation, scaling, and perspective

transformation (subject to renormalization).

Unlike other splines, NURBS can also represent conic sections

exactly.

272

Page 273: The full set of slides

Parametric bicubic surfaces

Cubic polynomials are also frequently used for piecewise inter-

polation of sets of points in a surface, for reasons similar to

those for curves.

In this case, the polynomials are functions of two parameters,

say, s and t.

For a parametric curve, the general equation is of the form

Q(s) = S · M · G, where the geometry matrix G is constant.

(Here we have used s as the parameter, for notational conve-

nience.)

If G is allowed to vary, say as a function of t,

Q(s, t) = S ·M · [G1(t) G2(t) G3(t) G4(t)]T

This would describe a family of curves, one for each value of t.

If each of the Gi(t) functions is cubic, then the resulting poly-

nomial can be represented as Gi(t) = T ·M ·Gi where

Gi = [gi,1 gi,2 gi,3 gi,4]T (i.e., each can be represented as a

vector with four components, corresponding to coefficients a, b,

c, and d of the polynomial.

Transposing the equation Gi(t) = T ·M ·Gi yields

Gi(t) = GTi ·MT · T T = [gi,1 gi,2 gi,3 gi,4] ·MT · T T

273

Page 274: The full set of slides

Substituting this in the original expression

Q(s, t) = S ·M · [G1(t) G2(t) G3(t) G4(t)]T ,

Q(s, t) = S ·M ·

g11 g12 g13 g14

g21 g22 g23 g24

g31 g32 g33 g34

g41 g42 g43 g44

·MT · T T

or

Q(s, t) = S ·M ·G ·MT · T T 0 ≤ s, t ≤ 1

Considering the x component only,

x(s, t) = S ·M ·Gx ·MT · T T

and the y and z components are similar.

274

Page 275: The full set of slides

Hermite surfaces

Like Hermite curves, Hermite surfaces are completely specified

by their geometry matrix — in this case, the 4× 4 matrix GH .

Considering only the x component, for the Hermite curve, we

had x(s) = S ·MH ·GHx

Rewriting (and noting that GHx is a function of t)

x(s, t) = S·MH ·GHx(t) = S·MH ·[

P1x(t) P4x(t) R1x(t) R4x(t)]T

P1x(t), and P4x(t) are the x components of the start and end

points of the curve over parameter s; R1x(t) and R4x(t) are the

derivatives at those points. (Note that 0 ≤ t ≤ 1, so there in a

continuum of those end points.)

Rewriting these constraints in Hermite form,

P1x(t) = T ·MH ·[

g11 g12 g13 g14

]T

x,

P4x(t) = T ·MH ·[

g21 g22 g23 g24

]T

x,

R1x(t) = T ·MH ·[

g31 g32 g33 g34

]T

x,

R4x(t) = T ·MH ·[

g41 g42 g43 g44

]T

x.

They can be written as a single equation[

P1x(t) P4x(t) R1x(t) R4x(t)]

= T ·MH ·GTHx

275

Page 276: The full set of slides

GHx =

g11 g12 g13 g14

g21 g22 g23 g24

g31 g32 g33 g34

g41 g42 g43 g44

x

Transposing both sides of the previous equation gives[

P1x(t) P4x(t) R1x(t) R4x(t)]T

= GHx ·MTH · T T

Substituting this in the expression for x(s, t) gives

x(s, t) = S ·MH ·GHx ·MTH · T T .

Similarly, for y(s, t) and z(s, t)

y(s, t) = S ·MH ·GHy ·MTH · T T

z(s, t) = S ·MH ·GHz ·MTH · T T

The matrix GHx can be calculated as:

GHx =

x(0, 0) x(0, 1) ∂∂sx(0, 0) ∂

∂sx(0, 1)

x(1, 0) x(1, 1) ∂∂sx(1, 0)

∂∂sx(1, 1)

∂∂tx(0, 0)

∂∂tx(0, 1)

∂2

∂s∂tx(0, 0)∂2

∂s∂tx(0, 1)∂∂tx(1, 0)

∂∂tx(1, 1)

∂2

∂s∂tx(1, 0)∂2

∂s∂tx(1, 1)

and similarly for GHy and GHz .

276

Page 277: The full set of slides

Bezier surfaces

In a similar manner to that used for Hermite surfaces, the bicu-

bic Bezier surfaces can be shown to be:

x(s, t) = S ·MB ·GBx ·MTB · T T

y(s, t) = S ·MB ·GBy ·MTB · T T

z(s, t) = S ·MB ·GBz ·MTB · T T

B-spline surfaces

Again similarly, for B-splines,

x(s, t) = S ·MBs ·GBsx ·MTBs · T T

y(s, t) = S ·MBs ·GBsy ·MTBs · T T

z(s, t) = S ·MBs ·GBsz ·MTBs · T T

Bicubic B-splines are also C2 continuous.

Other B-spline curves can also extended to surfaces similarly.

277

Page 278: The full set of slides

Normals to surfaces

Normals to surfaces can readily be found as the cross product

of the two tangent vectors to the surface. (The tangent vectors∂∂sQ(s, t) and ∂

∂tQ(s, t) are parallel to the surface at point (s, t).)

The tangent vectors can be calculated as

∂sQ(s, t) =

∂s(S ·M ·G ·MT · T T ) = ds(S) ·M ·G ·MT · T T

=[

3s2 2s 1 0]

·M ·G ·MT · ST

Similarly,

∂tQ(s, t) = S ·M ·G ·MT ·

[

3t2 2t 1 0]T

and the normal is

∂sQ(s, t)× ∂

∂tQ(s, t) = [yszt − ytzs zsxt − ztxs xsyt − xtys]

where xs is the x-component of the s tangent vector, . . .

The surface normal is a fifth degree polynomial of the two vari-

ables s and t.

278

Page 279: The full set of slides

Curves in OpenGL — Evaluators

OpenGL directly supports the drawing of curved surfaces di-

rectly through the use of evaluators.

Evaluators can construct curves and surfaces based on the Bern-

stein basis polynomials. (This includes Bezier curves and patches,

and B-splines.)

In order to draw curves and surfaces using other basis polyno-

mials (e.g., Hermite polynomials) the user program must trans-

form that basis to a Bernstein basis.

Evaluators are not difficult to use. Looking at the case of one

dimensional curves first, the following steps are performed:

• The function is defined with glMap1*()

• The function is enabled with glEnable()

(Both functions are usually called as part of the initializa-

tion.)

• The function is evaluated at a series of points using glEvalCoord1()

between a glBegin() and glEnd() block in the display()

function. [This is similar to using glVertex*().]

glEvalCoord1() is usually called repeatedly.

279

Page 280: The full set of slides

The function glMap1() defines a one-dimensional evaluator

that evaluates the Bernstein polynomial of order n + 1, where

n is the degree of the polynomial.

void glMap1fd(GLenum target, TYPE t1, TYPE t2, GLint

stride, GLint order, const TYPE *points);

target specifies what the control points represent (see the ta-

ble following), t1 and t2 specify the range for the variable t,

stride specifies the number of floats or doubles between the

beginning of one control point and the beginning of the next

one in the data structure referenced in points. This allows con-

trol points to be embedded in arbitrary data structures. (See

the man pages for more information.) order is the order of the

polynomial. *points is a pointer to the list of control points.

Parameter Meaning

GL_MAP1_VERTEX_3 x, y, z vertex coordinates

GL_MAP1_VERTEX_4 x, y, z, w vertex coordinates

GL_MAP1_INDEX color index

GL_MAP1_COLOR_4 R, G, B, A

GL_MAP1_NORMAL normal coordinates

GL_MAP1_TEXTURE_COORD_1 s texture coordinates

GL_MAP1_TEXTURE_COORD_2 s, t texture coordinates

GL_MAP1_TEXTURE_COORD_3 s, t, r texture coordinates

GL_MAP1_TEXTURE_COORD_4 s, t, r, q texture coordinates

280

Page 281: The full set of slides

void glEvalCoord1{fd}(TYPE t);

Evaluates the enabled one-dimensional maps. The argument t

is the value of the domain coordinate.

For evaluated vertices, generation of values for color, color in-

dex, normal vectors, and texture coordinates is done by evalu-

ation. Calls to glEvalCoord1() do not use the current values

for color, color index, normal vectors, and texture coordinates,

and leaves those values unchanged.

The following code is from the program bezcurve.c:

GLfloat ctrlpts[4][3] = { /* define control points */

{ -4.0, -4.0, 0.0}, { -2.0, 4.0, 0.0},

{2.0, -4.0, 0.0}, {4.0, 4.0, 0.0}};

int MAX_T = 35;

void init(void)

{

glClearColor(0.0, 0.0, 0.0, 0.0);

glShadeModel(GL_FLAT);

glMap1f(GL_MAP1_VERTEX_3,0.0,1.0,3,4, &ctrlpts[0][0]);

glEnable(GL_MAP1_VERTEX_3);

}

281

Page 282: The full set of slides

void display(void)

{

int i, t;

glClear(GL_COLOR_BUFFER_BIT);

glColor3f(1.0, 1.0, 1.0);

glBegin(GL_LINE_STRIP);

for (t = 0; t <= MAX_T; t++)

glEvalCoord1f((GLfloat) t/ (float)MAX_T);

glEnd();

/* Display the control points as dots. */

glPointSize(5.0);

glColor3f(0.0, 0.0, 1.0);

glBegin(GL_POINTS);

for (i = 0; i < 4; i++)

glVertex3fv(&ctrlpts[i][0]);

glEnd();

glFlush();

}

282

Page 283: The full set of slides

It is common for coordinate values to be evenly spaced, as they

were in the previous example.

In this case, the functions glMapGrid1() and glEvalMesh1()

are useful.

void glMapGrid1{fd}(GLint n, TYPE t1, TYPE t2);

Defines a grid that goes from t1 to t2 in n evenly spaced steps.

void glEvalMesh1(GLenum mode, GLint p1, GLint p2);

Applies the currently defined map grid to all enabled evaluators.

The mode can be either GL_POINT or GL_LINE, depending on

whether points or a connected line is required. The call has

exactly the same effect as issuing a glEvalCoord1() for each

of the steps from p1 to p2, inclusive.

283

Page 284: The full set of slides

The previous code could be rewritten as:

void init(void)

{

glClearColor(1.0, 1.0, 1.0, 0.0);

glShadeModel(GL_FLAT);

glMap1f(GL_MAP1_VERTEX_3,0.0,1.0,3,4,&ctrlpts[0][0]);

glMapGrid1f(MAX_T, 0.0, 1.0);

glEnable(GL_MAP1_VERTEX_3);

}

void display(void)

{

glClear(GL_COLOR_BUFFER_BIT);

glColor3f(1.0, 0.0, 0.0);

glEvalMesh1(GL_LINE, 0, MAX_T);

/* Display control points as dots. */

.

.

.

glFlush();

}

284

Page 285: The full set of slides

Two dimensional evaluators

For two dimensional evaluators, the process is similar, except

that there are two parameters, s and t:

• Define the evaluator(s) with glMap2*()

• Enable them by passing the appropriate value to glEnable()

• Invoke them either by calling glEvalCoord2() between a

glBegin and glEnd pair or by specifying and applying a

mesh with glMapGrid2() and glEvalMesh2().

void glMap2{fd}(GLenum target, TYPE s1, TYPE s2, GLint

sstride, GLint sorder, TYPE t1, TYPE t2, GLint tstride,

GLint torder, TYPE *points)

The target parameter is as shown in the earlier table, except

MAP1 is replaced by MAP2. s1, s2, t1, and t2 are the ranges

for s and t. sstride and tstride are the “distance” to the

next s and t values in the array of control points. sorder and

torder are the order parameters, which need not be the same.

For example, to use the 4 × 4 subset starting at [20][20] of

the following array

GLfloat ctrlpts[100][100][3];

tstride should be 3 and sstride should be 100.

*points should be set to &ctrlpts[20][20][0].

285

Page 286: The full set of slides

void glEvalCoord2{fd} (TYPE s, TYPE t);

Causes evaluation of the enabled two-dimensional maps. The

arguments s and t are values for the domain coordinates. If

either of the vertex evaluators is enabled (GL_MAP2_VERTEX_3

or GL_MAP2_VERTEX_4), then the normal to the surface is com-

puted analytically. This normal is associated with the gener-

ated vertex if automatic normal generation has been enabled

using glEnable(GL_AUTO_NORMAL). If it is disabled, the cor-

responding enabled normal map is used. If no such map exists,

the current normal is used.

void glMapGrid2{fd}(GLint ns, TYPE s1, TYPE s2,

GLint nt, TYPE t1, TYPE t2);

void glEvalMesh2(GLenum mode, GLint i1, GLint i2,

GLint j1, GLint j2);

Defines a two-dimensional map grid that goes from s1 to s2

in ns evenly spaced steps, goes from t1 to t2 in nt steps

(glMapGrid2*()), and then applies this grid to all enabled

evaluators (glEvalMesh2()). In glEvalMesh2()the mode pa-

rameter can be GL_FILL as well as GL_POINT or GL_LINE.

GL_FILL generates filled polygons.

286

Page 287: The full set of slides

The following examples are from bezmesh.c:

GLfloat ctrlpts[4][4][3] = {

{ {-1.5, -1.5, 4.0}, {-0.5, -1.5, 2.0},

{ 0.5, -1.5, -1.0}, { 1.5, -1.5, 2.0} },

{ {-1.5, -0.5, 1.0}, {-0.5, -0.5, 3.0},

{ 0.5, -0.5, 0.0}, { 1.5, -0.5, -1.0} },

{ {-1.5, 0.5, 4.0}, {-0.5, 0.5, 0.0},

{ 0.5, 0.5, 3.0}, { 1.5, 0.5, 4.0} },

{ {-1.5, 1.5, -2.0}, {-0.5, 1.5, -2.0},

{ 0.5, 1.5, 0.0}, { 1.5, 1.5, -1.0} }

};

void myinit(void)

{

glClearColor(0.0, 0.0, 0.0, 1.0);

glEnable(GL_DEPTH_TEST);

glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4,

0, 1, 12, 4, &ctrlpts[0][0][0]);

glEnable(GL_MAP2_VERTEX_3);

glEnable(GL_AUTO_NORMAL);

glEnable(GL_NORMALIZE);

glMapGrid2f(20, 0.0, 1.0, 20, 0.0, 1.0);

initlights(); /* for lighted version only */

}

287

Page 288: The full set of slides

void display(void)

{

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glPushMatrix();

glRotatef(85.0, 1.0, 1.0, 1.0);

glEvalMesh2(GL_FILL, 0, 20, 0, 20);

glPopMatrix();

glFlush();

}

288

Page 289: The full set of slides

Using NURBS objects

Using NURBS objects is relatively straightforward (the hard

part is actually capturing the object to be rendered):

• If lighting is to be used with a NURBS surface, call glEnable()

with argument GL_AUTO_NORMAL to generate surface nor-

mals automatically.

• Use gluNewNurbsRenderer() to create a pointer to a

NURBS object. which is referred to when creating the

NURBS curve or surface.

• If desired, call gluNurbsProperty() to choose rendering

values such as the maximum size of lines or polygons that

are used to render the NURBS object. gluNurbsProperty()

can also enable a mode where the tessellated geometric data

can be retrieved through a user-defined callback function.

• Call gluNurbsCallback() to be notified when an error is

encountered. (Error checking may slightly degrade perfor-

mance but is highly recommended.) gluNurbsCallback()

may also be used to register the functions to call to retrieve

the tessellated geometric data.

• Start the curve or surface by calling gluBeginCurve() or

gluBeginSurface().

289

Page 290: The full set of slides

• Generate and render the curve or surface. Call gluNurbsCurve()

or gluNurbsSurface() at least once with the control points

(rational or nonrational), knot sequence, and order of the

polynomial basis function for the NURBS object. These

functions may be called again to specify surface normals

and/or texture coordinates.

• Call gluEndCurve() or glulEndSurface() to complete

the curve or surface.

These functions are detailed in the man pages.

The following program (surface.c) shows an example of the

use of a NURBS object to render a surface. The surface is a

small symmetrical hill with control points ranging from -3.0 to

3.0. The knot sequence is non-uniform, with a multiplicity of 4

at each endpoint (making it equivalent to a Bezier patch):

/* Program surface.c - using NURBS */

GLfloat ctrlpts[4][4][3];

GLUnurbsObj *theNurb;

290

Page 291: The full set of slides

/* Initialize the control points */

void init_surface(void)

{

int s, t;

for (s = 0; s < 4; s++) {

for (t = 0; t < 4; t++) {

ctrlpts[s][t][0] = 2.0*((GLfloat)s - 1.5);

ctrlpts[s][t][1] = 2.0*((GLfloat)t - 1.5);

if ((s == 1 || s == 2) && (t == 1 || t == 2))

ctrlpts[s][t][2] = 3.0;

else

ctrlpts[s][t][2] = -3.0;

}

}

}

void CALLBACK nurbsError(GLenum errorCode)

{

const GLubyte *estring;

estring = gluErrorString(errorCode);

fprintf(stderr, "NURBS ERROR: %s\n", estring);

exit(0);

}

291

Page 292: The full set of slides

void myinit(void)

{

/* Initialize lighting and material properties */

GLfloat mat_diffuse[] = { 0.7, 0.7, 0.7, 1.0 };

GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };

GLfloat mat_shininess[] = { 100.0 };

.

.

glEnable(GL_AUTO_NORMAL);

glEnable(GL_NORMALIZE);

init_surface();

theNurb = gluNewNurbsRenderer();

gluNurbsProperty(theNurb,GLU_SAMPLING_TOLERANCE,25.0);

gluNurbsProperty(theNurb, GLU_DISPLAY_MODE, GLU_FILL);

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

glTranslatef (0.0, 0.0, -4.0);

}

292

Page 293: The full set of slides

void display(void)

{

GLfloat knots[8] = {0.0,0.0,0.0,0.0,1.0,1.0,1.0,1.0};

int i, j;

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glPushMatrix();

glRotatef(330.0, 1.,0.,0.);

glScalef (0.25, 0.25, 0.25);

gluBeginSurface(theNurb);

gluNurbsSurface(theNurb, 8, knots, 8, knots,

4 * 3, 3, &ctrlpts[0][0][0],

4, 4, GL_MAP2_VERTEX_3);

gluEndSurface(theNurb);

glEnable(GL_LIGHTING);

glPopMatrix();

glutSwapBuffers();

293

Page 294: The full set of slides

294

Page 295: The full set of slides

Lighting and color

Light and vision are intimately related. The way in which our

eyes “see” light is fundamental to the reproduction of color (and

monochrome) images.

Intensity resolution

The eye is sensitive to relative differences in the intensity of

light, implying that the eye has a logarithmic response to light

intensity. Small relative intensity changes are not detectable;

the threshold for detection in the range of a ratio of 1.01.

The eye has a very wide dynamic range, and for low light levels

the response is different than at high light levels. (At the lowest

light levels, our vision is black and white only; there is no color

sensation.)

A natural question is “how many intensity levels can be seen?”

It makes sense to look at the dynamic range of the medium

to answer this. The best films have a dynamic range of around

1000, as does a very good monitor. (The eye itself has a dynamic

range of about 106.)

Solving (1.01)n = 1000 gives n ≈ 700 for film.

(1.01)n = 106 gives n ≈ 1400 for the eye.

295

Page 296: The full set of slides

256 intensity values, distributed logarithmically, gives a dy-

namic range of (1.01)256 = 12.8, which is about the dynamic

range for newsprint.

Generally, then, about 10 bits of intensity are required to pro-

duce a very high quality dynamic range.

Spatial resolution

The eye also has a minimum spatial resolution, below which two

distinct points “blur together” and become indistinguishable.

This is useful in the printing process, and in the design of CRT’s,

because it allows the eye to be “fooled” into seeing different

intensities by displaying small filled patterns.

For example, to display 5 levels of gray on a device capable of

only the solid colors black and white (a bitonal device), a grid

of 4 “pixels” might be used, as follows:

Devices like laser printers are bitonal. Photographs and CRT

displays are capable of displaying “shades of gray” directly, and

can give much better picture quality with larger pixels.

296

Page 297: The full set of slides

297

Page 298: The full set of slides

Color

Color perception is often referred to in terms of three quantities

— hue, saturation, and lightness (or brightness).

Hue is the actual color (red, yellow, green, purple, etc.)

Saturation is the intensity of the color — how far it is from a

gray of equal intensity. (Pink is red with low saturation.)

Lightness (or brightness, for a luminous object) is the perceived

intensity of the light from the object, regardless of the color.

This model is usually called the HSV model, where V (for

Value) is the lightness component, Hue is measured in degrees,

−1 ≤ S ≤ 1 and 0 ≤ V ≤ 1.

HS

yellow

red (0)

magenta

green (120)

cyan white

0.0

1.0

V

black

Blue (240)

298

Page 299: The full set of slides

In 1931, theCommission Internationale de l’ Eclairage (CIE)

defined a standard for the mixing of colors which is still in use.

0.1 0.2 0.3 0.4 0.5 0.6 0.7

0.1

0.2

0.3

0.4

0.5

0.6

0.7

0.8

400

500

520

560

580

600

700

x

y

B

A

CD

E

G

F480

540

The dominant color of the point at A is that of color B. The

dominant color of F is the complement of the color B. D and E

are complementary colors.

A line in this diagram represents the colors obtainable by mixing

together the colors at its endpoints. The interior of a triangle

represents all the colors attainable by mixing together the colors

at the vertices.

299

Page 300: The full set of slides

The human eye

The structure of the human eye is shown in the following dia-

gram. Note that it is a relatively simple optical system consist-

ing of a lens, and a “screen” (the retina) onto which an image is

projected. The center of the field of view, the macula or fovea,

has the best visual acuity.

The retina is made up of two types of cells — rods and cones.

The rods are more sensitive than the cones, but do not detect

color. The macula has a high density of cones, and the cones

here are smaller (accounting for the visual acuity in this area).

The rods are more predominant in the periphery.

300

Page 301: The full set of slides

There are 3 types of cones, each sensitive to a different part of

the spectrum. Combinations of the different spectral sensitivi-

ties of the cones are what allows us to see in color. (Color blind

people are deficient in one or more of the cone types.)

.02

.04

.06

.08

.10

.12

.14

.18

400 440 480 520 560 600 720680640

.20

in the human eyeColor response of the 3 types of cones

Wavelength (nm)

A mixture of the three primary colors red, green, and blue in

varying intensities allows us to produce visually the sensation

of almost any color.

301

Page 302: The full set of slides

Physical properties of light

Light consists of photons — “particles” with no mass which

travel at the speed of light. They have energy, and one measure

of this energy is the “wavelength” of the light.

To a very good approximation, light travels in straight lines,

and behaves much like a particle. (In some circumstances, light

has wave properties — interference and diffraction effects —

but these are rarely important in computer graphics.)

Light may be “bent” or refracted in transparent substances,

and the degree of bending, or refraction, is related to a quantity

called the refractive index.

When light is incident on a shiny, flat surface, it is reflected.

The reflected light leaves the surface at an angle such that the

angle between the incident light and a normal to the surface is

equal to the same angle for the reflected light.

normal

incident light reflected light

refracted lightincident light

302

Page 303: The full set of slides

Light can also be absorbed by materials. This gives the material

color — light which is not absorbed is reflected or transmitted,

giving the material its color. For example if a transparent mate-

rial, say glass, has chromium added to it, it appears red, because

the red light is transmitted but other colors are absorbed.

On the other hand, if an opaque material absorbs red, it will

appear to have the color cyan. It it absorbs blue, it will appear

yellow; if it absorbs green, then magenta. (Yellow, cyan, and

magenta dyes are often used in printing instead of red, green,

and blue.)

A black body absorbs all the light that it receives.

Materials may also emit light. Generally, light is emitted when

material is heated to a high enough temperature (“black body

radiation”) e.g., an incandescent light.

Light is also emitted when some of its atoms or molecules are

“excited” — energy is added by, say, an electric field. Examples

of this are fluorescent lights, and LEDs.

303

Page 304: The full set of slides

Physical properties of surfaces

Surfaces have different characteristics with their interaction with

light. Some surfaces are “shiny” and reflect most of the light

incident on the surface. Others are dull and reflect little light

at all.

Surfaces have “color” — the color comes from the light incident

on the surface, which may reflect some spectral components,

and absorb others. A red painted cube reflects red light, but

absorbs green and blue light. If illuminated by pure green or

blue light, the cube would appear black.

Some surfaces, like the CRT screen, for example, emit their own

light, in varying colors and intensities.

Characteristics like “roughness” are also important in determin-

ing the interaction between light and a surface. Some materials

are translucent, in varying degrees, or may have a transparent

coating which changes the appearance. (Often a wet rock is

more colorful than a dry rock, for example.)

Transparency may be wavelength dependent. Filters may trans-

mit red light, say, and absorb green and blue light.

304

Page 305: The full set of slides

Light sources — illumination

The light by which we see can come from a number of sources,

with many different properties. The normal background light

in an indoor scene is white light (ignoring such characteristics

as “color temperature” which relate to the spectral properties

of the light), and is generally not very directional, since it is

scattered from many surfaces — walls, floor, etc.

This is usually called ambient light, and its main property is

that it seems to come from all directions. When ambient light

interacts with a surface, it is scattered in all directions.

Light which is strongly directional (bright sunlight, or a laser

beam) is said to be specular. When interacting with a shiny

surface, most of this light is reflected as from a mirror. (Specu-

larity is really a material property, but in graphics the term is

also applied to light sources.)

The diffuse component of light comes from one direction, so it

is brighter if it shines directly on a surface than if it meets a

surface at an angle, but it is scattered at all angles from the

surface.

305

Page 306: The full set of slides

DiffuseAmbient Specular

ambient — rays come from and scatter into all directions

diffuse — rays come from one direction, scatter into all direc-

tions

specular — rays come from one direction, reflect into one di-

rection

For specular light, the normal to the surface is used to calculate

the direction into which the reflected light is scattered.

Light also has the property that it reduces in intensity with

distance from the source. In particular, light from a point source

decreased in intensity proportional to the square of the distance

from the source.

For a long linear light source, the intensity decreases propor-

tional to the distance from the source.

Other light sources (spotlights, and other lights containing re-

flecting or lens elements) may have more complex functions.

306

Page 307: The full set of slides

Rendering lit scenes realistically

We require a simple model for lighting which allows us to model

both light sources and surface properties in a reasonable way.

Ideally, we would assign the appropriate physical properties to

each surface in the scene, and calculate the interaction of every

visible ray of light from the source to the viewer’s eye position.

This, of course, would produce a very realistic image (if all the

properties of a surface — specularity, roughness, transparency,

etc.) were well modelled, but would be very computationally

expensive.

This can be done, in fact, at least for relatively simple scenes,

and the method is called ray tracing. It can produce very

realistic images, including refraction, shadows and multiple re-

flections, but at very high computational cost.

OpenGL uses a relatively simple model for lighting. It defines

a set of properties for materials, a set of light sources, and a

lighting model. This model cannot model secondary effects like

shadows or reflected illumination, but it produces reasonable

illumination effects and is computationally efficient.

307

Page 308: The full set of slides

Lighting models in OpenGL

In order to add lighting to a scene, the following steps are re-

quired:

• Define normals at each vertex for every object.

• Create and position one or more light sources with glLight*().

(OpenGL supports at least 8 light sources).

• Select a lighting model; with glLightModel*(). This de-

fines the level of (global) ambient light, and the effective

location of the viewpoint (for the lighting calculations).

• Define the material properties for each object in the scene

with glMaterial*().

void glLight{if}(GLenum light, GLenum pname,

TYPE param);

void glLight{[if}v(GLenum light, GLenum pname,

TYPE *param);

create the light specified by GL_LIGHT0, GL_LIGHT1, . . . with

properties given by pname; param specifies the set of parameters

for the properties pname. (The scalar version can only set single

value parameters.)

308

Page 309: The full set of slides

Parameter name Default Value Meaning

GL_AMBIENT (0.0, 0.0, 0.0, 1.0) ambient light

intensity

GL_DIFFUSE (1.0, 1.0, 1.0, 1.0) diffuse light in-

tensity

GL_SPECULAR (1.0, 1.0, 1.0, 1.0) specular light

intensity

GL_POSITION (0.0, 0.0, 1.0, 0.0) (x, y, z, w) po-

sition of light

GL_SPOT_DIRECTION (0.0, 0.0, -1.0) (x, y, z) direc-

tion of spotlight

GL_SPOT_EXPONENT 0.0 spotlight expo-

nent

GL_SPOT_CUTOFF 180.0 spotlight cutoff

angle

GL_CONSTANT_ATTENUATION 1.0 see equation

following

GL_LINEAR_ATTENUATION 0.0

GL_QUADRATIC_ATTENUATION 0.0

The default values for GL_DIFFUSE and GL_SPECULAR are for

GL_LIGHT0 only. Other lights default to black (0.0, 0.0, 0.0, 1.0)

The tutorial lightposition shows a simple use of lighting and

the interaction with viewing position.

309

Page 310: The full set of slides

OpenGL treats a light source like a geometric primitive; it is af-

fected by the modelview matrix, and stored in eye coordinates.

The light source can be moved by changing the modelview ma-

trix. The projection matrix has no effect on a light source.

The attenuation is calculated from the expression

attenuation factor =1

kc + kld + kqd2

where d is the distance from the light source, and kc, kl, and

kq are the constant, linear, and quadratic attenuation terms,

respectively.

The OpenGL lighting model has four components:

1. The global ambient light intensity

2. The viewpoint position (local or at infinity)

3. Whether front and back surfaces should have different light-

ing calculations

4. Whether or not the specular color should be separated from

ambient and diffuse colors and applied after texturing.

All those properties can be set individually with the function

glLightModel*().

310

Page 311: The full set of slides

void glLightModel{if}(GLenum pname, TYPE param);

void glLightModel{if}v(GLenum pname, TYPE *param);

Again, pname specifies the property; param specifies the set of

parameters for the property pname.

In the following, GL_LIGHT_MODEL precedes the parameter name:

Parameter name Default Value Meaning

_AMBIENT (0.2, 0.2, 0.2, 1.0) ambient intensity for

scene

_LOCAL_VIEWER 0.0 how specular reflec-

tion angles are calcu-

lated

_TWO_SIDE 0.0 one sides or two sided

lighting

_COLOR_CONTROL GL_SINGLE_COLOR whether specular

color is calculated

separately

The argument GL_SEPARATE_SPECULAR_COLOR causes the spec-

ular color component to be calculated separately, and added af-

ter texturing. This can enhance highlights, and make a picture

look more realistic. The function call is:

glLightModeli{GL_LIGHT_COLOR_CONTROL,

GL_SEPARATE_SPECULAR_COLOR}

311

Page 312: The full set of slides

The following simple program light.c shows a lighted sphere:

/* Initialize material property, light source,

* lighting model, and depth buffer. */

void init(void)

{

GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };

GLfloat mat_shininess[] = { 50.0 };

GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };

GLfloat white_light_[] = { 1.0, 1.0, 1.0, 0.0 };

glClearColor (0.0, 0.0, 0.0, 0.0);

glShadeModel (GL_SMOOTH);

glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);

glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);

glLightfv(GL_LIGHT0, GL_POSITION, light_position);

glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light);

glLightfv(GL_LIGHT0, GL_SPECULAR, white_light);

glEnable(GL_LIGHTING);

glEnable(GL_LIGHT0);

glEnable(GL_DEPTH_TEST);

}

312

Page 313: The full set of slides

void display(void)

{

glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glutSolidSphere (1.0, 20, 16);

glFlush ();

}

313

Page 314: The full set of slides

Recall that light sources are treated as geometric primitives,

so a light source can be moved by transforming the modelview

matrix. The movelight program shows the effect of moving

the light source and keeping the object and viewpoint fixed:

314

Page 315: The full set of slides

Material properties

Material properties are set with the function glMaterial*().

It has the form:

void glMaterial{if}(GLenum face, GLenum pname, TYPE

param);

void glMaterial{if}v(GLenum face, GLenum pname, TYPE

*param);

where face can be GL_FRONT, GL_BACK, or GL_FRONT_AND_BACK,

and pname and param are defined in the following table:

Parameter name Default Value Meaning

GL_AMBIENT (0.2, 0.2, 0.2, 1.0) ambient color of material

GL_DIFFUSE (0.8, 0.8, 0.8, 1.0) diffuse color of material

GL_SPECULAR (0.0, 0.0, 0.0, 1.0) specular color of material

GL_EMISSION (0.0, 0.0, 0.0, 1.0) emissive color of material

GL_SHININESS 0.0 specular exponent

See the man pages for color index parameters.

The parameter GL_EMISSION which allows a body to emit light

(for modelling lamps, etc.) Light from this source does not

illuminate any part of the scene.

The tutorial lightmaterial shows interaction between light

sources and material properties.

315

Page 316: The full set of slides

The program material.c shows several material properties:

The spheres in the first row have materials with no ambient

reflection, the second row has significant ambient reflection, and

the third row has colored ambient reflection.

The first column has blue, diffuse reflection only. The second

and third add specular reflection with a low and high shininess

exponent, respectively. The fourth includes emission.

316

Page 317: The full set of slides

How a vertex’s color is calculated in OpenGL

The color of a vertex under lighting in OpenGL is:

color = emission at the vertex +

global ambient light scaled by the ambient co-

efficient of the material +

the ambient, diffuse, and specular components

from all light sources, properly attenuated

The color components are calculated as:

color = emissionmaterial+

ambientlight model × ambientmaterial +∑n−1i=0

(

1kc+kld+kqd2

)

× (spotlight effect)i ×[ambientlight × ambientmaterial +

(max{Li · n, 0})× diffuselight × diffusematerial +

(max{s · n, 0})shininess × specularlight × specularmaterial

]

i

where n = (nx,ny,nz) is the unit normal vector at the vertex,

L = (Lx,Ly,Lz) is the unit vector pointing from the vertex

to the light, and s = (sx, sy, sz) is the unit vector obtained by

normalizing the sum of the unit vectors between the vertex and

the light position, and the vertex and the viewpoint.

The result is clamped to [0, 1].

317

Page 318: The full set of slides

Polygon shading— what happens between vertices?

Clearly, the color and intensity of light at interior points in the

polygon can be calculated similarly. This would be computa-

tionally expensive, however, so other methods are used.

The most common approach is to shade each polygon patch a

constant color, or to apply some kind of interpolation. Con-

stant shading tends to look poor — the edges of the polygon

are apparent even when the polygon patches are small. This is

because of the Mach effect. This is due to an intensity depen-

dent lateral inhibition of the receptors in the eye. This tends

to accentuate an edge where there is a discontinuous change in

the intensity.

Even interpolated shading is somewhat subject to this effect.

Gouraud shading

OpenGL directly supports Gouraud shading. Here, the inten-

sity (and color) is linearly interpolated from the values at the

vertices. In fact, this interpolation can be incorporated in a

scan line algorithm.

OpenGL directly supports only flat shading and Gouraud shad-

ing. Recall, however, that many transformations can also be

applied to the color vector.

318

Page 319: The full set of slides

Phong shading

Instead of interpolating over the intensity, Phong shading inter-

polates over the normal vectors at the vertices. This generally

produces a better specular component, because a better approx-

imation is used to the normal at each point. (Gouraud shading

tends to “average out” specular highlights.)

This is computationally expensive, however, because the in-

terpolated normal vector must be normalized at each point.

Some techniques can reduce this calculation (e.g., approximat-

ing Phong shading by a Taylor series expansion) but it is still

computationally expensive.

Other interpolated shading models have also been used.

There are some problems with interpolated shading, however.

One of the most interesting is the problem of shared vertices.

In the following, unless vertex B is added to the polygon on the

left, there may be a discontinuity in intensity and color at that

point.

A

C

B

319

Page 320: The full set of slides

Surface detail

So far, the surfaces we have seen are “mathematically smooth.”

This simply does not occur in the real world; all surfaces have

some characteristics — roughness, color variation, etc.

One such “surface realism enhancement” supported by OpenGL

is textures.

Textures are rectangular arrays of data — bitmaps, which are

applied to a surface. Individual elements are called texels. They

are transformed as part of the surface, so they generally behave

as part of the scene. For example, a texture might be a brick

wall, scanned from a picture of a real wall. If the texture were

applied to a surface, then the perspective and other transfor-

mations would apply to the components of the texture on the

wall — they would scale properly.

Many textures are scanned images, partly because it is tedious

to “program” a bit image. Textures are interesting because they

are often transformed to non-rectangular regions. Doing this in

a reasonable way can be challenging.

Textures can be one, two, or three dimensional. They can be

used in several different ways — as “wallpaper” covering a sur-

face; as a means to modulate the color a surface would have

otherwise had, or to blend a texture color with the surface color.

320

Page 321: The full set of slides

Texture mapping

The following are the basic steps to use texture mapping:

• Create a texture object.

• Specify a texture for the object.

• Indicate how the texture is to be applied to each pixel.

• Enable texture mapping.

• Draw the scene, specifying both texture and geometric co-

ordinates.

Create a texture object and specify a texture

The data describing a texture may have one, two, three, or four

elements per texel, It usually represents an (R,G,B,A) value.

They are often scanned pictures, but the example following

shows a simple checkerboard constructed with code.

Indicate how the texture is to be applied to each

pixel

There are several possible ways to use the texture data at a

pixel: the texture can replace the color value, the texture can

modulate (scale) the color, or a constant color can be blended

with the pixel, based on the texture value.

321

Page 322: The full set of slides

Enable texture mapping

Texturing is enabled with the functionglEnable(), with argu-

ments GL_TEXTURE_1D, GL_TEXTURE_2D, or GL_TEXTURE_3D

It is disabled with glDisable().

Draw the scene, specifying both texture and geo-

metric coordinates

The texture must be aligned with the primitive to which it is to

be applied. Therefore, both texture and geometric coordinates

must be specified. For a 2-dimensional texture map, the texture

range is 0.0 to 1.0 in both dimensions, but the geometric coor-

dinates can be anything. Also, if the texture values are outside

[0.0, 1.0] what happens? (Are they repeated or set to a fixed

value?)

The following program (checker.c) shows the use of simple

texturing on two surfaces:

322

Page 323: The full set of slides

/* Create checkerboard texture */

#define checkImageWidth 64

#define checkImageHeight 64

GLubyte checkImage[checkImageWidth][checkImageHeight][4];

static GLuint texName;

void makeCheckImage(void)

{

int i, j, c;

for (i = 0; i < checkImageWidth; i++) {

for (j = 0; j < checkImageHeight; j++) {

c = ((((i&0x8)==0)^((j&0x8))==0))*255;

checkImage[i][j][0] = (GLubyte) c;

checkImage[i][j][1] = (GLubyte) c;

checkImage[i][j][2] = (GLubyte) c;

checkImage[i][j][3] = (GLubyte) 255;

}

}

}

323

Page 324: The full set of slides

void myinit(void)

{ glClearColor (1.0, 1.0, 1.0, 0.0);

glShadeModel(GL_FLAT);

glEnable(GL_DEPTH_TEST);

makeCheckImage();

glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

glGenTextures(1, &texName);

glBindTexture(GL_TEXTURE_2D, texName);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,

GL_REPEAT);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,

GL_REPEAT);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,

GL_NEAREST);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,

GL_NEAREST);

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,

GL_DECAL);

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,

checkImageWidth, checkImageHeight, 0, GL_RGBA,

GL_UNSIGNED_BYTE, checkImage);

}

324

Page 325: The full set of slides

void display(void)

{

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glEnable(GL_TEXTURE_2D);

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,

GL_REPLACE);

glBindTexture(GL_TEXTURE_2D, texName);

glBegin(GL_QUADS);

glTexCoord2f(0.0, 0.0); glVertex3f(-2.0, -1.0, 0.0);

glTexCoord2f(0.0, 1.0); glVertex3f(-2.0, 1.0, 0.0);

glTexCoord2f(1.0, 1.0); glVertex3f(0.0, 1.0, 0.0);

glTexCoord2f(1.0, 0.0); glVertex3f(0.0, -1.0, 0.0);

glTexCoord2f(0.0, 0.0); glVertex3f(1.0, -1.0, 0.0);

glTexCoord2f(0.0, 1.0); glVertex3f(1.0, 1.0, 0.0);

glTexCoord2f(1.0, 1.0); glVertex3f(2.414, 1.0,-1.414);

glTexCoord2f(1.0, 0.0); glVertex3f(2.414,-1.0,-1.414);

glEnd();

glFlush();

glutSwapBuffers();

glDisable(GL_TEXTURE_2D);

}

325

Page 326: The full set of slides

Using textures

We will look at two-dimensional textures. One- and three-

dimensional textures are specified similarly, and details can be

found in the man pages.

The function

void glTexImage2D(Glenum target, GLint level, GLint

internalFormat, GLsizei width, GLsizei height, GLint

border, GLenum format, GLenum type, const GLvoid *texels);

defines a two-dimensional texture. The parameter target is

set to GL_TEXTURE_2D or GL_PROXY_TEXTURE_2D; level is

normally set to 0, internalFormat has 38 possibilities, but

GL_RGBA is common (see the man pages for others), width and

height are the dimensions of the texture image, border is the

width of the border, 0 (no border) or 1. The minimum size

of a texture map is at least 64 × 64 (66 × 66 with borders).

format and type define the way the pixels are represented (see

man pages for glDrawPixels()). The parameter *texels is

a pointer to the texture data image.

The number of texels for both width and height must be a power

of 2.

326

Page 327: The full set of slides

glTexParameter{if}(GLenum target, GLenum pname, GLenum

param);

glTexParameter{if}v(GLenum target, GLenum pname, GLenum

*param);

set various parameters that control how a texture object is

treated as it is applied to a fragment, or stored in a texture

object. target is GL_TEXTURE_2D, and pname and param can

have the following values:

Parameter Values

GL_TEXTURE_WRAP_S GL_CLAMP, GL_CLAMP_TO_EDGE,

GL_REPEAT

GL_TEXTURE_WRAP_T GL_CLAMP, GL_CLAMP_TO_EDGE,

GL_REPEAT

GL_TEXTURE_WRAP_R GL_CLAMP, GL_CLAMP_TO_EDGE,

GL_REPEAT

GL_TEXTURE_MAG_FILTER GL_NEAREST, GL_LINEAR

GL_TEXTURE_BORDER_COLOR GL_NEAREST, GL_LINEAR

GL_TEXTURE_PRIORITY [0.0, 1.0] for the current texture

object

GL_TEXTURE_MIN_LOD any real number

GL_TEXTURE_MAX_LOD any real number

GL_TEXTURE_BASE_LEVEL any non-negative integer

GL_TEXTURE_MAX_LEVEL any non-negative integer

327

Page 328: The full set of slides

So far, we have looked at textures as “wallpaper” to be painted

on a surface. The function

glTexEnv{if}(GLenum target, GLenum pname, TYPE param);

glTexEnv{if}v(GLenum target, GLenum pname, TYPE *param);

sets the current texturing function. targetmust be GL_TEXTURE_ENV.

pname can be GL_TEXTURE_ENV_MODE or GL_TEXTURE_ENV_COLOR.

For GL_TEXTURE_ENV_MODE, param can be GL_REPLACE, GL_DECAL,

GL_MODULATE, or GL_BLEND.

GL_REPLACE replaces the color of the fragment with the tex-

ture, GL_DECAL linearly interpolates the texture color with the

fragment color using the A value of the texture. The A value

of the pixel is the value from the fragment.

The other two parameters depend on the base internal format

of the texture.

For GL_TEXTURE_ENV_COLOR, param is an array of 4 real num-

bers representing R, B, G, and A.

There are 6 base internal formats, GL_ALPHA, GL_LUMINANCE,

GL_LUMINANCE_ALPHA, GL_INTENSITY, GL_RGB, and GL_RGBA.

For details, see the man pages.

The tutorial on texturing by Nate Robbins shows much of the

functionality of textures.

328

Page 329: The full set of slides

Textures can be mapped on surfaces of virtually any shape.

The program texsurf.c shows a (colorful) texture mapped

onto a curved surface:

GLfloat ctlpts[4][4][3] = {

{{ -1.5, -1.5, 4.0}, { -0.5, -1.5, 2.0},

{0.5, -1.5, -1.0}, {1.5, -1.5, 2.0}},

{{ -1.5, -0.5, 1.0}, { -0.5, -0.5, 3.0},

{0.5, -0.5, 0.0}, {1.5, -0.5, -1.0}},

{{ -1.5, 0.5, 4.0}, { -0.5, 0.5, 0.0},

{0.5, 0.5, 3.0}, {1.5, 0.5, 4.0}},

{{ -1.5, 1.5, -2.0}, { -0.5, 1.5, -2.0},

{0.5, 1.5, 0.0}, {1.5, 1.5, -1.0}}

};

GLfloat texpts[2][2][2] = {{{0.0, 0.0}, {0.0, 1.0}},

{{1.0, 0.0}, {1.0, 1.0}}};

void display(void)

{

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glColor3f(1.0, 1.0, 1.0);

glEvalMesh2(GL_FILL, 0, 20, 0, 20);

glFlush();

329

Page 330: The full set of slides

}

#define imageWidth 64

#define imageHeight 64

GLubyte image[3*imageWidth*imageHeight];

void makeImage(void)

{

int i, j;

float ti, tj;

for (i = 0; i < imageWidth; i++) {

ti = 2.0*3.14159265*i/imageWidth;

for (j = 0; j < imageHeight; j++) {

tj = 2.0*3.14159265*j/imageHeight;

image[3*(imageHeight*i+j)] = (GLubyte) 127*

(1.0+sin(ti));

image[3*(imageHeight*i+j)+1] = (GLubyte) 127*

(1.0+cos(2*tj));

image[3*(imageHeight*i+j)+2] = (GLubyte) 127*

(1.0+cos(ti+tj));

}

}

}

330

Page 331: The full set of slides

void myinit(void)

{

glClearColor(1.0, 1.0, 1.0, 0.0);

glMap2f(GL_MAP2_VERTEX_3,0,1,3,4,0,1,12,4, ctlpts);

glMap2f(GL_MAP2_TEXTURE_COORD_2, 0, 1, 2, 2, 0, 1,

4, 2, texpts);

glEnable(GL_MAP2_TEXTURE_COORD_2);

glEnable(GL_MAP2_VERTEX_3);

glMapGrid2f(20, 0.0, 1.0, 20, 0.0, 1.0);

makeImage();

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,

GL_DECAL);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,

GL_REPEAT);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,

GL_REPEAT);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,

GL_NEAREST);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,

GL_NEAREST);

331

Page 332: The full set of slides

glTexImage2D(GL_TEXTURE_2D, 0, 3, imageWidth,

imageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);

glEnable(GL_TEXTURE_2D);

glEnable(GL_DEPTH_TEST);

glEnable(GL_NORMALIZE);

glShadeModel (GL_FLAT);

}

332

Page 333: The full set of slides

Depth perception — ordering of primitives

One of the considerations which must be made when rendering

3-dimensional figures is the perception of depth — depending on

the point of view, objects may obscure each other. Clearly, an

object “further away” from the viewpoint should be obscured

by a closer object. There are a number of ways to accomplish

this.

One of the most obvious is to sort objects by distance from the

viewpoint. The objects are then rendered in order of distance,

starting with the furthest. For simple, non-intersecting convex

objects, this is satisfactory, but problems can arise if the objects

are more complex.

What object is in front here? (Looking from page bottom.)

333

Page 334: The full set of slides

It is always possible to decompose a scene into non-intersecting

convex objects, but the computational effort may be large, and

there may be many small fragments. The required data struc-

ture for the objects would be complex.

Another possibility is to sort the surface primitives in depth

order. Note that, in either case, the sorting has to be done for

each line of view.

A number of optimizations can be applied to reduce the over-

all complexity; for example, objects or sets of objects can be

contained within a simple bounding volume and, if they do not

intersect, the whole volume can be ordered. This can be applied

recursively, using data structures like the oct-tree structure, or

other spatial partitioning structures.

Sometimes, structures can be constructed hierarchically, so that

a higher level in the hierarchy provides a bounding volume for

lower levels in the hierarchy.

One advantage of this type of algorithm is that effects like trans-

parency can be handled easily.

334

Page 335: The full set of slides

The z-buffer algorithm

One of the most common methods for determining visible sur-

faces is the z-buffer algorithm. Essentially, the depth of the

point represented by a pixel is recorded in a memory location

(the z-buffer), with one z-buffer location for each pixel. The

color of the pixel is overwritten only if the current depth value

is less than that presently written in the buffer.

With this algorithm, the order of rendering of primitives is ir-

relevant, if all primitives have solid colors.

This algorithm is often implemented in hardware — it requires

another buffer (memory location) for each pixel. (Most current

graphics boards have sufficient memory for this function.)

OpenGL implements a depth buffer algorithm. The function

void glDepthFunc(GLenum func);

is the appropriate function call.

The parameter func can have values: GL_NEVER, GL_ALWAYS,

GL_LESS, GL_LEQUAL, GL_EQUAL, GL_GEQUAL, GL_GREATER,

or GL_NOTEQUAL.

Whether or not the function is actually implemented in hard-

ware depends primarily on the driver provided with the graphics

card.

335

Page 336: The full set of slides

Ray tracing

Ray tracing uses a simple physical model for the interaction

between light and matter. It automatically incorporates hidden

surface removal, and recursive ray tracing handles transparency,

reflections, and shadows in a physically realistic way.

The basic algorithm is simple. A ray is traced (backward) from

the eye through the image plane until it interacts with an object.

The pixel in the image plane is then given the color of the object

with the ray intersects. Simple ray tracing follows a single ray

from the eye to a light source.

Light source

Pixels

planeImage

336

Page 337: The full set of slides

Recursive ray tracing allows a ray to split into components at

each surface, and each ray can be traced to a source. In the

following, the cube is transparent, while the sphere and cylinder

are solid.

Imageplane

Pixels

Light source

Here there are direct and indirect rays from the eye to the source

— direct rays are reflected from one surface; indirect rays are

reflected from or refracted by multiple surfaces.

337

Page 338: The full set of slides

Note that, in naive ray tracing, each ray must be checked for

intersection with each primitive. In practice, it is usually pos-

sible to design a hierarchical data structure which can reduce

the number of intersection tests. Also, multiple rays should be

projected through each pixel.

The multiple reflections and refractions can be represented in a

tree structure; the following shows such a tree for the previous

example:

sphere

cube

cylinder

eye

One of the keys to ray tracing is the ability to calculate in-

tersections between rays and primitives. For many types of

curved surfaces, this calculation is non-trivial. For polygons

and spheres it is not difficult, and a closed form is available for

general quadric surfaces.

338

Page 339: The full set of slides

Ray/circle intersections

Intersections between a sphere and a ray can be calculated effi-

ciently (consequently almost every ray trace picture contains a

number of solid or transparent spheres).

Given a ray with endpoints (x1, y1, z1) and (x2, y2, z2) the ray

can be expressed parametrically as

x = x1 + t(x2 − x1) = x1 + it

y = y1 + y(y2 − y1) = y1 + jt

z = z1 + t(z2 − z1) = z1 + kt

A sphere with centre (l,m, n) of radius r is given by

(x− l)2 + (y −m)2 + (z − n)2 = r2

t = 0

t = 1

Ray start (x ,y ,z )

Ray end (x ,y ,z )

1

2 2

1 1

2

339

Page 340: The full set of slides

substituting for x, y, and z gives a quadric in t

at2 + bt + c = 0 where

a = i2 + j2 + k2

b = 2i(x1 − l) + 2j(y1 −m) + 2k(z1 − n)

c = l2 +m2 + n2 + x21 + y21 + z21 − 2(lx1 +my1 + nz1 + r2)

If the roots are not real, then the line does not intersect the

sphere. Otherwise, the roots give the front and back entry

points of the ray.

Since both the front and back entry points are readily calculated

for a sphere, transparent spheres are also oftem modelled in ray

traced images.

The normal to the surface at the intersection point can be cal-

culated as

N =

xi − l

r,yi −m

r,zi − n

r

340

Page 341: The full set of slides

Ray/polygon intersections

Here, a straightforward approach is to:

1. Find an equation for the plane containing the polygon

2. Check for intersection between the ray and the plane

3. Check whether the point is interior to the polygon

If the plane is expressed in standard form

ax + by + cz + d = 0

and the ray defined parametrically, then the intersection is

t = −ax1 + by1 + cz1 + d

ai + bj + ck

A test for the point being interior is to sum the angles between

lines drawn to the vertices — it is 360◦ for an interior point.

Ray/box intersections

Here, the clipping algorithms discussed earlier are effective.

Boxes are important because they are used as bounding volumes

for other primitives in a hierarchical data structure.

341

Page 342: The full set of slides

Ray/quadric intersections

The general equation for a quadric is

Ax2+2Bxy+2Cxz+2Dx+Ey2+2Fyz+2Gy+Hz2+2Iz+J = 0

which is equivalent to

[x, y, z, 1] ·

A B C D

B E F G

C F H I

D G I J

·

x

y

z

1

= 0

Substituting for x, y, and z from the parametric equation for

the ray again gives a quadratic equation with coefficients

a = Ax2d + 2Bxdyd + 2Cxdzd + Ey2d + 2Fydzd +Hz2d

b = 2(Ax1xd+B(x1yd+xdy1)+C(x1zd+xdz1)+dxd+Ey1yd+

F (y1zd + ydz1) +Gyd +Hz1zd + Izd)

c = Ax21+2Bx1y1+2Cx1z1+2Dx1+Ey21 +2Fy1z1+2Gy1+

Hz21 + 2Iz1 + J

where (xd, yd, zd) is the normalized ray direction.

342

Page 343: The full set of slides

One useful optimization is to treat the ray as a cone of rays

subtending some small spherical angle.

This may be feasible, but the computation of the intersection

of a surface with a cone, and the subsequent reflections, is more

complex.

Ray tracing produces images of amazing clarity, but they are

generally not realistic. Again, there is the problem of interac-

tion with a mathematically perfect surface. In the “real world”

rays are not perfectly reflected and refracted, and there is some

intensity loss at every surface.

There are also second order effects not modelled by this sim-

ple recursive ray tracing. For example, specular reflection of

ambient light can cause a dimunition of shadows.

343

Page 344: The full set of slides

Modelling solids

We have been looking at ways of representing the surfaces of an

object, without any discussion of its interior. For visualization

applications only, this is sufficient.

Many applications require a more complete representation of

a solid. For example, mechanical drawing systems may want

to determine if two components overlap, or satisfy some other

physical constraint.

OpenGL does not support the representation of solid bodies; if

required, this is done with a higher level of software.

There are many ways of representing solids; in general, repre-

sentations which allow the combination of objects to form other

objects are desirable.

Note that the combination operations should be guaranteed to

produce solid objects themselves.

A further desirable constraint on a representation is that the

representation not admit an invalid representation.

OpenGL represents solids as a set of surfaces. There is no guar-

antee that all surfaces meet at the edges, or that a “solid” is

completely covered with surfaces — not a desirable representa-

tion for solids.

344

Page 345: The full set of slides

Regularized Boolean set operations

The most elementary operations for combining primitives are

the Boolean set operations — union, intersection, and differ-

ence.

In the case of solids, the regular Boolean operations may not

always leave a solid as the result — e.g., the intersection of two

solids touching only at a vertex would leave a point.

The regularized Boolean set operators, denoted as ∪∗, ∩∗,and −∗ always yield solids when applied to solids. (e.g., the

intersection of two solids touching only at a vertex would yield

a null object.)

Basically, regularized Boolean set operators are ordinary Boolean

set operators applied to the interior of solids. Formally, the

regularized operations can be defined as

A op∗ B = closure(interior(A op B))

where op is one of the set operations ∪, ∩, or −.

Consider what happens when objects relate as follows:

3 421 5 6

345

Page 346: The full set of slides

Primitive instancing

This is a common way of modelling solids in 3-D CAD pack-

ages in specific application areas. The primitives are parame-

terized with a small number of parameters. In microelectronics,

transistors, resistors, and capacitors might be some of the pa-

rameterized items. Parameters could be physical such as size,

number of connections, etc., or electrical; e.g. current capacity,

voltage rating.

In mechanical systems, such things as bolts, screws, gears, etc.

could be parameterized with quantities like size, pitch, etc.

The primitives are defined in code, and there is no consistent

method for combining the primitives to make more complex

objects.

Pitch

size

346

Page 347: The full set of slides

Boundary representations

Here, solids are represented as closed solid figures. A common

representation primitive is the polyhedron.

Simple polyhedra satisfy Euler’s formula, V −E+F = 2, where

V is the number of vertices E is the number of edges, and F is

the number of faces.

For 2-manifolds (surfaces which can be covered locally by a

small disc — generally, non-intersecting surfaces) which have

faces with holes, this generalizes to V −E+F −H = 2(C−G)

where H is the number of holes in the faces, G is the number

of holes that pass through the object, and C is the number of

separate components.

Not a 2-manifold V - E + F - H = 2(C - G)24 36 15 1 13

A set of Euler operators can be defined to transform solids by

adding and removing edges, vertices, etc. within this constraint.

347

Page 348: The full set of slides

Spatial partitioning

Spatial-occupancy enumeration — voxels

Like pixels in a plane, voxels are rectangular solid volumes ar-

ranged in a 3-dimensional grid. Much of our discussion of the

advantages and disadvantages of a pixel (raster) representation

carries over into 3 dimensions. In fact, matters are more com-

plex, because the number of voxels grows as n3, rather than

n2.

This representation is normally used when a small, fixed volume

is to be modelled. For example, it is often used in medical

imaging, and for numerical modelling of some physical processes

(fluid flow, etc.)

Given a fine enough resolution, it is capable of showing fine

spatial variations in such things as density or flow rate.

348

Page 349: The full set of slides

Octrees

These are merely tree data structures recursively subdividing a

region until the variation of interest is “small enough.”

The initial volume is divided into 8 identical sub-volumes, and

this is repeated recursively. It is an extension of the 2-dimensional

quadtree representation for surfaces.

Subdivision is performed until there is no additional information

added by the next subdivision.

This “divide and conquer” structure is efficient, and has been

applied in many domains. The n log n nature of the spatial

representation has, for example, been used in the evaluation of

pairwise interactions of long-range force interactions (gravity),

reducing calculations from O(n2) to O(n log n).

A primary advantage of the octree representation is the body

of knowledge (and code) relating to tree manipulation. In fact,

the regularized Boolean set operations can be implemented with

little difficulty.

349

Page 350: The full set of slides

Binary Space-Partitioning Trees

It is also possible to divide a volume into irregular sized subvol-

umes, simply using a plane at arbitrary orientation and position.

This can be done recursively, much like the octree representa-

tion, and can be much more efficient.

Considering the previous (planar) example, three lines are suf-

ficient to achieve a full partitioning. This may well work far

better for other examples with non-rectangular objects.

Constructive Solid Geometry

Here, simple geometric primitives are used to construct more

complex structures. In general, the structure forms a hierarchy

(a tree) similar to the hierarchical structures to construct an

image. Of course, the primitives here are solids, not surfaces.

Again, the regularized Boolean set operators are used for com-

position. The utility of this representation depends on the do-

main of interest and the richness of the primitive set.

350