Go to Bottom of Page

[Previous Section] [Next Section]

LECTURE 2
TOPICS IN OPENGL
TOPICS IN OPENGL

TOPICS IN OPENGL

We consider the OpenGL model of Transformations, and how it is used. We will also consider its pixel map functionalities.

[Previous Section] [Next Section]

1   Transformations

There are several coordinate systems in OpenGL and it is important to keep them straight. We have:

1.1  World Coordinates and Local Coordinates.

When you construct a geometric object, it is placed in this coordinate system. To illustrate this idea, it is enough to consider 2D. Suppose you want to model a house, whose outline is just given by a hexagon:

	glBegin(GL_POLYGON);
	  glVertex2d (0, 0);
	  glVertex2d (w, 0);
	  glVertex2d (w, h);
	  glVertex2d (w-rw, h+rh);
	  glVertex2d (rw, h+rh);
	  glVertex2d (0, h);
	glEnd();
	

Here w, h represents the width and height of the house, and rw, rh are parameters related specifying the roof of the house. But what if you want to have several copies of this house in our scene? We may want to scale and rotated the house to rest on a hill slope, etc. We will see how to do this. This means we want to think of the overall model (``the world'' or ``the scene'') as comprising one or more local objects (like the house above). For this reason, we sometimes call the coordinate system of the house the local coordinates. A local coordinate can be instanced in the world coordinate system any number of times.

OpenGL gives us two sets of tools to achieve this. (1) Display Lists and (2) Matrix Transformations. Here is a simple example: instancing houses from the previous lecture. In particular, notice how we wrap the object within a display list. In the program, it was a cube, but it could be our house, with some unique identifier MY_HOUSE. Then we call to display it this:

	glPushMatrix();
	glLoadIdentity();
	glRotatef(theta, 0.0, 0.0, 1.0); // angle theta
	glScalef(2.0, 2.0, 2.0); 	// double its size
	glCallList(MY_HOUSE);
	glPopMatrix();
	

What is the matrix being manipulated? This is the MODELVIEW matrix, described below. There is also a PROJECTION matrix.

More about Display Lists.

1.2  Window Coordinates.

This is perhaps the easiest to understand among the coordinate systems. For one thing, it is 2D. It corresponds to the physical display in a one-to-one manner. This is a integer-based coordinate system, with each integer point (x,y) corresponding to a pixel on the screen.

In OpenGL we have two slight complications: first, modern displays do not just take over the entire screen. So each application is given control of a portion of the screen, which we call a window. But since the word ``window'' is over-used, we may say screen window to be specific. The coordinate system within each window is called its window coordinates.

Following graphics convention, window coordinates for increasing x-coordinates goes from left to right, and increasing y-coordinates goes from top to bottom. Thus, the top-left corner of a screen window is denoted (0,0) in the window-coordinates. Note that in mathematical conventions, y-coordinates goes from bottom to top. OpenGL will use both conventions, depending on whether we are in screen coordinates or not.

The second complication is this: in OpenGL, each window itself can be used for several subwindows called viewports. So, what OpenGL displays is really in a specific viewport in a specific screen window! There is no need for a viewport to even fit in a window, but then you will not see those parts that do not fit.

We remark that the screen windows need not be disjoint among themselves. The way to display any screen window is therefore through some priority mechanism based on ``depth'' (above/below relation).

It is tempting to thinkg of viewports as subwindows within a screen window. But it is simpler than this, and amounts to a ``current transformation'' which we will explain.

Here is how you set up a screen window in OpenGL/GLUT:

	// SETTING UP A SCREEN WINDOW
	glutInit(argc, argv);		// C/C++ command line arguments 
	glutCreateWindow("My Screen Window"); // window with title
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
	glutInitWindowSize(640, 480);	// screen width and height in pixels
	glutInitWindowPosition(0,0);	// position of top-left window corner 
	

The first two steps are mandatory, but the next three steps have default values that are assigned if you do not specify them.

Question: where is the viewport in the above in the above setup? If we do not specify it, it defaults to the entire window!

	// SETTING UP VIEW PORT 
	glViewport(GLint x, GLint y, GLsizei w, GLsizei h);
		// (x,y) is the top-left corner of viewport
		// in screen window coordinates
	

Here is a typical use of viewport: suppose you have an image to display and it has a particular aspect ratio R = Width/Height. If the user resize the window to a different aspect ratio, the image will be distorted. To avoid this, you set up the display callback function to use a viewport with the correct ratio R, and that is maximized to fit the current window. See [Hill, Chap.3.2.2, p.92].

1.3  Eye Coordinates

REFERENCE: [Hill] (Parallel projection in Sect 5.6.2, p.263, Perspective projection in Section 7.2).

After subjecting our models to a series of tranformations, we have a model transformation matrix M. Each point p is transformed to Mp in world corrdinates.

So far, this modeling is independent of the eye position. For instance, if the eye were to look in the z direction, and is placed at (1,0,0), then relative to the viewpoint of the eye, each Vertex (x,y,z) appears as (x1, y, z). Thus the position of the eye, in eye coordinates is at (0,0,0). In the simplest case, this amounts to a translation. But in general, this is specified by a view tranformation matrix V.

Then a point p is now transformed to VMp. In OpenGL, the combination of V and M is called the modelview matrix. Now the name ``Model + View'' is clear.

There are several convenient API's for setting up V. In general, the eye parameters can be specified by three vectors:

eye-position eye=(eyex, eyey, eyez),
eye-direction dir=(dirx, diry, dirz),
vertical-direction up=(upx, upy, upz).
In OpenGL, eye-direction is replaced by ``look-at point'' at=(atx, aty, atz); for instance, we may assume (at = eye+dir).

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(eye_x, eye_y, eye_z,
		at_x, at_y, at_z,
		up_x, up_y, up_z);
	
NOTE that this transformation (i.e., V) is applied after all the model transformations (i.e., M) is applied.

1.4  View Volume

The world coordinate system is potentially infinite and 3D. We have now to worry about projecting it to a finite 2D viewport. The first step is to make the world finite. This depends on the viewing parameters, of which the previous eye parameters is only one part.

What is missing is the choice of projection, view angle, and visual limits (how far or how near we can see). These parameters are needed to create an image. Here, we need to distinguish two basic projections: parallel projection and perspective projection. Mathematically, they can be unified, but in practice, we keep them apart.

The desired portion is called the view volume which is geometrically a frustum. Once we specify this volume, we can map this volume into our viewport. This is the projection operator, again specified by a matrix P. A point p is then transformed as
PVMp = P(VM)p

Again, we have API to help us define the view frustum. The simpliest is a rectangular parallelepiped (with a left-side, right-side, top-side and bottom-side), cut off by a near plane and a far plane.

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glFrustum(xmin, xmax, ymin, ymax, zmin, zmax);
	glMatrixMode(GL_MODELVIEW);	// restore default matrix
	

Note that these coordinates are relative to the eye (the origin). This is not a completely general frustum, clearly. But there is no assumed symmetry about the origin. The near plane is z=zmin, and the far plane is z=zmax, so they are assumed parallel to the z=0 plane.

Alternatively, if we need a perspective view, we need a frustum with non-parallel faces. The corresponding API is

	gluPerspective(fov, aspect, near, far);
	
The parameters near, far are similar to zmin, zmax before. The fov parameter (field-of-view) here specifies the vertical angle spanned by the eye, symmetric in the horizontal direction. The value of aspect gives us the aspect ratio (which combined with fov) gives us the horizontal angle spanned at the eye. ``Vertical'' and ``horizontal'' makes sense relative to the up direction, which recall, is part of the eye parameters.

	glOrtho(left, right, bott, top, near, far);
	

Now, this volume can be transformed so that it has a particular location in world coordinates. This transformation is called the eye-coordinates. If we are only interested in 2D images, we can simplify this using an API in the GLU library. We have already seen examples of this usage:

	gluOrtho2D(left, right, bott, top);
	

WHAT ABOUT THE VIEW PLANE?   To some extent, it does not matter what this is. The main reason is that we ALWAYS ASSUME IN OPENGL that this plane is normal to the z-axis of the eye coordinates. Thus the choice of the choice of the viewplane, intersected against the view frustum, only determines the size of the image. But this is already automatically controlled by our viewport transformation. There is ONE subtlety: under projective transformation, if this viewplane is behind the eye position, then everything appears inverted. But the default is to choose some plane in front of the eye.

1.5  The OpenGL Pipeline

The objects we construct (which ultimately reduces to Vertices) are subject to a sequence of transformations, as illustrated in the OpenGL specs.

  • The original coordinates of your point p (glVertex*) after the Model-View transformation VM is said to be in eye-coordinates, VMp.
  • After applying the projection matrix P, the point PVMp is in clip coordinates. Actually, we do not clip until the next step.
  • The step called ``Perspective Division'' is only applicable in perspective projection. It is a null step for orthogonal projection. Basically, it amounts to de-homogenizing our coordinates. Emerging from this, we have normalized coordinates. This is so-called because the view frustum is now transformed into a cube centered at the origin, with side lengths 2. Hill calls this the canonical view volume (CVV).
  • At this point, we can clip away the geometry that will not be visible. Clipping of points is easy - discard a point if it does not lie in the CVV. This happens exactly when any of its coordinates has absolute value greater than 1. Lines and polygons are slightly more involved and may introduce new vertices and edges. Actually, since vertices have attributes such as normals and colors, we need to compute them for the new vertices as well.
  • There are some subtle points that may call for clipping BEFORE we normalize. Read [Hill, p.389] for details.
  • Finally, the points (x,y,z) in normalized coordinates are transformed using the viewport transformation into screen coordinates (x, y,z) where z is depth information that may be used in different ways.

EXERCISES  

Q1. Determine all the default screen window parameters in GLUT.

Q2. How do you use several windows in GLUT? See below.

Q3. Study the Camera class introduced by Hill (section 7.3, p.366).

[Previous Section] [Next Section]

2   Applications of Transformations

We can use Transformations to achieve the following effects:

  • Zooming and Panning
  • Animation
  • Fly-over or walk-through
  • Tiling

[Previous Section] [Next Section]

3   Lighting

[Previous Section] [Next Section]

4   ADDITIONAL NOTES

[Previous Section] [Next Section]

5   Working with Subwindows

Suppose you have created a window called mainWin:

	int mainWin;
	mainWin = glutCreateWindow(argv[0]);
	

Note that when we have only one window, we ignore the unique integer identifier that is returned by glutCreateWindow(); but you now need to refer to this identifier. Here is how you can create a subwindow called button which is supposed to be a little square which turns on or off in response to mouse clicks.

  // ON/OFF button
  button = glutCreateSubWindow(mainWin, 300, 0, 20, 20);  // size = 20x20 
  glClearColor(0.0, 0.0, 0.5, 0.0);   // background color
  glutDisplayFunc(displayButton);     // callback for displaying subwindow
  glutReshapeFunc( buttonReshape );   // callback for reshaping subwindow
  glutSetWindow(mainWin);	      // reset the focus to main window

In the presence of several windows, GLUT needs to know which subwindow is the ``focus'' of attention at any moment; The last function glutSetWindow() above is used to set the focus.

Go back to text.

[Previous Section] [Next Section]

6   Getting Started

Go to Top of Page


File translated from TEX by TTH, version 3.01.
On 26 Sep 2001, 17:57.