Chapter 2
Basics
OpenGL majors in three-dimensional graphics, but computer monitors operate with 
essentially flat, two-dimensional screens. Representing a three-dimensional space on a two-
dimensional surface requires projection. Each point in the space projects mathematically to an 
appropriate point on the surface. This chapter introduces OpenGL commands to identify the 
part of the window to receive the projection, to set up the projection, and to draw an object. 
Included with the projection is a clipping volume, outside of which no drawing happens. 
These foundational concepts and commands permit drawing something besides a mere 
background. This chapter also brings another Delphi event handler into use.
Use the first chapter’s final code as the starting point in this chapter. If you have not already 
done so, create a new directory under the OpenGL directory and name it Chapter.2. In 
Delphi load the First.Dpr project and do a File|Save Project As in the new directory, naming 
it Rect.Dpr. Do File|Save As for the First1.Pas unit. Name it Rect1.Pas in the new directory.
Viewport
Event Handler
Drawing a green background is a wonderful accomplishment, but by itself, a green 
background is not very exciting. Drawing a shape on the background is much more 
interesting. For now, drawing a rectangle is the next great advance in OpenGL knowledge. 
The place for drawing is the form’s OnPaint event handler, but setting up the drawing area 
also needs the OnResize handler. Go to the Events page of the form’s Object Inspector and 
double-click OnResize. In the event handler place a call to glViewport, a call to glOrtho, and 
a modicum of error checking:


PAGE 13
 procedure TForm1.FormResize(Sender: TObject);	
begin	
  if not openGLReady then	
    exit;	
  glViewport(0,0,ClientWidth,ClientHeight);	
  glOrtho(-1.0,1.0,-1.0,1.0,-1.0,1.0);	
	
  errorCode := glGetError;	
  if errorCode<>GL_NO_ERROR then	
    raise Exception.Create('FormResize:'+gluErrorString(errorCode));	
end;	
Declaration
Remember that OpenGL is fairly platform-independent, so it does not inherently know 
anything about the coordinate system of the operating environment. Use glViewport to 
make a connection between the coordinates used by the windowing system (Microsoft 
Windows) and the coordinates used by OpenGL. Its declaration looks like this:

procedure glViewport(x,y:GLint; width,height:GLsizei); stdcall;	
GLint and GLsizei both map to integer. The meaning of width and height are as expected, 
but x and y need more attention. Windows measures from the upper left corner of a region, 
but OpenGL measures from the lower left, as if it were relating to the first quadrant of a set 
of Cartesian coordinates. The main thing to note is that the y direction runs opposite to the 
familiar Windows coordinates, but fits more naturally with coordinates commonly used in 
mathematics.
If you are building your own interface unit, Listing 2-1 encompasses the declarations used 
thus far, plus a few more that are just waiting in the wings. Add other declarations when 
they appear.

unit OpenGL;

interface

uses
  Windows;

const
  GL_COLOR_BUFFER_BIT = $4000;
  GL_NO_ERROR = 0;
Type
  HGLRC = THandle;
  GLenum = cardinal;
  GLbitfield = cardinal;


PAGE 14
   GLfloat = single;
  GLclampf = single;
  GLint = integer;
  GLsizei = integer;

{regular commands}
procedure glClear(mask:GLbitfield); stdcall;
procedure glClearColor(red,green,blue,alpha:GLclampf); stdcall;
function  glGetError: GLenum; stdcall;
procedure glViewport(x,y:GLint; width,height: GLsizei); stdcall;

{utility commands}
function gluErrorString(errCode:GLenum):PChar; stdcall;

implementation

{regular commands}
procedure glClear; external opengl32;
procedure glClearColor; external opengl32;
function glGetError; external opengl32;
procedure glViewport; external opengl32;

{utility commands}
function gluErrorString; external glu32;

initialization
  Set8087CW($133F);
end.
Listing 2-1. Beginning of an interface unit.
Analogy
Think of the form as a wall. Use glViewport to cut a hole in the wall through which to view a 
scene. In this case, cut a hole the size of the entire client area of the form, so all that is left is 
the frame. A smaller “opening” is possible:

  glViewport(50,30,80,70);	
The above example starts at 50 pixels from the left side of the form and 30 pixels up from the 
bottom of the form. It is 80 pixels wide and 70 pixels tall (going up from the bottom). It may 
not be a good idea to specify such absolute coordinates without specifying a non-resizable 
border or limiting the minimum window size.

PAGE 15
 
? Note:
glViewport limits the portion of the window in which the scene is rendered, but does not 
restrict the area in which the background is cleared. In the wall analogy, think of the 
background color as something sprayed onto the stage for the scene. Not only does the spray 
go through the hole in the wall, but the spray also covers the entire wall!
In simplest terms, rendering is drawing with OpenGL. To render a scene is to use OpenGL 
constructs and commands to cause pixels to illuminate in a pattern that represents the scene 
envisioned by the programmer.
Orthographic Projection
Meaning
The call to glOrtho defines a clipping volume, which is a region in space in which objects can 
be seen. Any part of a scene that lies outside of the region is not visible (clipped). Of course, 
though the rendered scenes may be conceptually three-dimensional, they must be projected 
onto a two-dimensional screen. The available projections are orthographic projection and 
perspective projection. (You get one guess as to which kind glOrtho uses.) In orthographic 
projection, also known as parallel projection, an object appears the same size whether it is 
near or far from the viewer. Showing a simple, flat rectangle certainly does not need 
anything more complicated. Here is the declaration:

procedure glOrtho(left,right,bottom,top,zNear,zFar: GLdouble); stdcall;	
GLdouble is the same thing as a Delphi double, an IEEE 64-bit floating-point value.

  GLdouble = double;	
Left, right, bottom, and top describe the same area as the glViewport, but with programmer-
defined values for the edges. The rendering commands use values relative to these. In the 
above FormResize method listed above in the Event Handler section, –1.0 is the new value 
given to the left side of the viewport, and 1.0 is the value for the right side. The bottom of the 
viewport is now –1.0, and the top is 1.0. The z values represent directions perpendicular to 
the screen. The zNear value represents the z-axis coordinate of the clipping region face 
nearest the viewer, while zFar designates the face farthest from the viewer, deepest into the 
screen.  Those values represent in three-dimensional space whatever the programmer wants 
them to mean.

PAGE 16
 Flexibility
Here is another example of glOrtho with glViewport:

  glViewport(0,0,ClientWidth,ClientHeight);	
  glOrtho(3.204,17.2,-11.0,1.45,5.025,41.0);	
The call to glViewport remains the same to isolate the effect, in this example, of changing the 
values given to glOrtho. For example, the clipping region now has a value of 17.2 for its right 
side. It is still located at the same place as before, which is the right side of the viewport, at 
ClientWidth. The value of 17.2 was just convenient. Delphi’s Object Pascal language has a 
similar concept for arrays:

var	
  first: array[0..9] of integer;	
  second: array[1..10] of integer;	
  third: array[100..109] of integer;	
All three of the above variables are equivalent arrays of ten integers, but with different 
starting points. Why not just use zero-based arrays? After all, that is all that is available in 
some programming languages. The answer is that they are declared in a manner convenient 
for the programmer. An alternate form may allow less typing in the coding of an algorithm, 
or may more obviously represent some real-world concept.

PAGE 17
 Similarly the programmer has the freedom to assign arbitrary values to the orthographic 
clipping region. He may choose values for computational convenience or for better 
conceptual representation of the scene. There is a little bit of order imposed on the glOrtho 
universe, however. Negative z values are in front of the viewport in the window, and 
positive z-values are behind the viewport. Placing the clipping region entirely in front of the 
viewport strains the hole-in-the-wall analogy, but the comparison has already served its 
purpose.
Drawing
Command Placement
At last the time has arrived to draw something: a rectangle. As expected, the place to draw 
the rectangle is in the OnPaint event handler. The drawing commands need to appear 
between glBegin and glEnd; the latter requires no parameters while, the former takes a 
parameter to set the drawing mode:

procedure glBegin(mode:GLenum); stdcall;	
procedure glEnd; stdcall;	
Between glBegin and glEnd goes a list of vertices, and possibly a few commands related to 
the vertices. Each vertex is simply a point in space specified by the glVertex command. This 
command takes many forms, but only a few of them are appropriate here:

procedure glVertex2f (x,y: GLfloat); stdcall;	
procedure glVertex3f (x,y,z: GLfloat); stdcall;	
procedure glVertex4f (x,y,z,w: GLfloat); stdcall;	
State Variables
In the FormPaint method (see Listing 2-2, below), not only does glClearColor set the clear 
color (background), but glColor sets the foreground color for drawing. These calls set state 
variables which remain the same until similar calls explicitly change them. If the command 
says to draw in blue, all drawing is done in blue until another call changes the state. 
OpenGL’s state variables are in DLLs, not directly accessible to the programmer. You read 
and write OpenGL’s state variables via function and procedure calls. The glColor command 
is before glBegin in the current event handler method, but it is among the limited set of 
commands that can appear between glBegin and glEnd, since color changes are often 
necessary in the midst of a drawing sequence.


PAGE 18
 procedure TForm1.FormPaint(Sender: TObject);
begin
  if not openGLReady then
    exit;
  {green background}
  glClearColor(0.0,0.4,0.0,0.0);
  glClear(GL_COLOR_BUFFER_BIT);

  {red rectangle}
  glColor3f(0.5,0.0,0.0);
  glBegin(GL_POLYGON);
    glVertex2f(0.25,0.25);
    glVertex2f(0.75,0.25);
    glVertex2f(0.75,0.75);
    glVertex2f(0.25,0.75);
  glEnd;
  glFlush;

  errorCode := glGetError;
  if errorCode<>GL_NO_ERROR then
    raise Exception.Create(‘Error in Paint’#13+
      gluErrorString(errorCode));
end;
Listing 2-2. Setting state variables in FormPaint.
Here is the declaration of the glColor command used in the FormPaint method:

procedure glColor3f(red,green,blue:GLfloat); stdcall;	
The new constant has this declaration:

  GL_POLYGON = 9;	
Since glColor3f has arguments of 0.5, 0.0, and 0.0, OpenGL is drawing in half-intensity red. 
Alpha is not specified, so it gets 1.0 as a default. In the calls to glVertex2f, no z value is 
passed, so all of the rectangle lies in the z = 0.0 plane. That means the rectangle lies in the 
same plane as the viewport, neither in front nor behind. Recall that glOrtho received 
arguments of –1.0 for left and 1.0 for right. Since the x values in glVertex are all positive, the 
rectangle is entirely on the right side of the viewing area. The same is true of the y values, so 
the rectangle lies entirely in the upper half of the viewing area. Remember that all the 
drawing coordinates are relative to the values defining the clipping region, so changing the 
arguments to glOrtho could affect the size, shape, and position of the drawing.

PAGE 19
 Drawing Mode
Note the argument passed to glBegin. GL_POLYGON informs OpenGL that the subsequent 
vertex list establishes the corners of a polygon. The number of vertices between glEnd and 
glBegin is the number of vertices in the polygon, which in this case is a quadrilateral. The first 
side of the polygon is a line from the first vertex to the second. The second side of the 
polygon is a line from the second vertex to the third, and so on until the polygon closes with 
a line from the last vertex to the first. OpenGL knows to do all that simply by being given 
GL_POLYGON. It further knows to fill in the interior of the polygon with the color 
previously specified in glColor. What if a complete scene needs more than one type of 
figure? Make multiple glBegin/glEnd pairs, passing the appropriate mode arguments.
Another new command introduced here is glFlush. For the sake of efficiency, the graphics 
engine does not necessarily start drawing as soon as it receives its first command. It may 
continue to accumulate information in a buffer until given a little kick. The call to glFlush 
forces it to start drawing.

  procedure glFlush; stdcall;	
To illustrate another drawing mode, GL_LINES, Listing 2-3 is the code for the OnPaint event 
handler that produced Figure 2-1, including a couple of new commands needed for that 
purpose.

  {white background}
  glClearColor(1.0,1.0,1.0,0.0);
  glClear(GL_COLOR_BUFFER_BIT);

  {top of the form}
  glColor3f(0.0,0.0,1.0);
  glRectf(-300.0,300.0,300.0,345.0);

  glLineWidth(2.0);
  glColor3f(0.0,0.0,0.0);
  glBegin(GL_LINES);
    {left side}
    glVertex2f(-300.0,-300.0);
    glVertex2f(-300.0,300.0);

    {bottom}
    glVertex2f(-300.0,-300.0);
    glVertex2f(300.0,-300.0);

    {right side}
    glVertex2f(300.0,-300.0);
    glVertex2f(300.0,300.0);
  glEnd;


PAGE 20
             
  glLineWidth(1.0);
  glBegin(GL_LINES);
    {view port measure left to right}
    glVertex2f(-300.0,-340.0); {line}
    glVertex2f(300.0,-340.0);

    glVertex2f(-300.0,-320.0); {end}
    glVertex2f(-300.0,-360.0);

    glVertex2f(-300.0,-340.0); {end}
    glVertex2f(-290.0,-330.0);

    glVertex2f(-300.0,-340.0); {arrow}
    glVertex2f(-290.0,-350.0);

    glVertex2f(300.0,-320.0); {arrow}
    glVertex2f(300.0,-360.0);

    glVertex2f(300.0,-340.0); {arrow}
    glVertex2f(290.0,-330.0);

    glVertex2f(300.0,-340.0); {arrow}
    glVertex2f(290.0,-350.0);

    {viewport measure bottom to top}
    glVertex2f(-325.0,-300.0); {line}
    glVertex2f(-325.0,300.0);

    glVertex2f(-312.0,-300.0); {end}
    glVertex2f(-337.0,-300.0);

    glVertex2f(-312.0,300.0); {end}
    glVertex2f(-337.0,300.0);

    glVertex2f(-325.0,-300.0); {arrow}
    glVertex2f(-332.0,-285.0);

    glVertex2f(-325.0,-300.0); {arrow}
    glVertex2f(-318.0,-285.0);

    glVertex2f(-325.0,300.0); {arrow}
    glVertex2f(-332.0,285.0);

    glVertex2f(-325.0,300.0); {arrow}
    glVertex2f(-318.0,285.0);

    {ortho measure left to right}
    glVertex2f(-300.0,-260.0); {line}


PAGE 21
     glVertex2f(300.0,-260.0);

    glVertex2f(-300.0,-260.0); {arrow}
    glVertex2f(-288.0,-272.0);

    glVertex2f(-300.0,-260.0); {arrow}
    glVertex2f(-290.0,-250.0);

    glVertex2f(300.0,-260.0); {arrow}
    glVertex2f(290.0,-250.0);

    glVertex2f(300.0,-260.0); {arrow}
    glVertex2f(288.0,-272.0);

    {ortho measure bottom to top}
    glVertex2f(-275.0,-300.0); {line}
    glVertex2f(-275.0,300.0);

    glVertex2f(-275.0,-300.0); {arrow}
    glVertex2f(-282.0,-285.0);

    glVertex2f(-275.0,-300.0); {arrow}
    glVertex2f(-268.0,-285.0);

    glVertex2f(-275.0,300.0); {arrow}
    glVertex2f(-282.0,285.0);

    glVertex2f(-275.0,300.0); {arrow}
    glVertex2f(-268.0,285.0);
  glEnd;
Listing 2-3. Using GL_LINES.
When the drawing mode is GL_LINES, each pair of calls to glVertex defines a line segment. 
The declaration for the new mode is:

GL_LINES = 1;	
Rectangles are so common that OpenGL has a special command just for drawing them. The 
first two parameters define one vertex, and the next two define the opposite vertex. The 
rectangle assumes z = 0. The float version just introduced is one of many variations. Also 
introduced is a command for setting the width of the lines in pixels. The default width is 1.0.

procedure glRectf(x1,y1,x2,y2:GLfloat); stdcall;	
procedure glLineWidth(width:GLfloat); stdcall;	

PAGE 22
 Additional Considerations
Naming Convention
You may have noticed by now that all the OpenGL command names begin with “gl.” The 
graphics library has a well-defined naming convention, which this book mentions from time 
to time. So far the “gl” prefix has dominated, which does apply to all OpenGL commands. A 
“glu” prefix has also made a cameo appearance. It is the standard for all commands of the 
utility library. Chapter 1 presented the “wgl” prefix for the extensions to Microsoft Windows 
that support OpenGL rendering. Similarly the X Window System (for UNIX) extensions use 
a “glx” prefix, and OS/2 Presentation Manager extensions use “pgl.”
The naming convention does not stop with prefixes. The glVertex commands, for example, 
have a number suffix, such as 2, 3, or 4, indicating the number of parameters the command 
requires. A later chapter explains the fourth (w) parameter, but for now, if it receives no 
explicit value, it receives a value of 1.0. In the two-parameter version, even the z value goes 
without assignment, so it gets a 0.0 value by default. The “f” suffix indicates these versions 
of glVertex take GLfloat parameters. Other parameter types are possible, so as many as 24 
different versions of the glVertex command are available.
The FormPaint method used the glColor3f command. The “3” indicates that this version of 
the command takes three parameters, but there is another version of the command that takes 
four parameters. The “f” indicates that the parameters are of type GLfloat.
Error Handling in Depth
Chapter 1 introduced glGetError, but gave it only superficial treatment. Checking it every 
time a command could possibly produce an error would have a performance penalty. Notice 
that FormPaint performs a single call to glGetError at the end of the method. Fortunately the 
graphics library is capable of storing multiple error codes. If several error codes have -
accumulated, multiple calls to glGetError are required to clear them all. Accordingly, this 
application replaces its default exception handler. Place this at the end of the private section 
of the form’s type declaration:

procedure ExceptionGL(Sender:TObject;E:Exception);	
The application’s OnException event handler activates whenever an exception climbs all the 
way up the stack without being handled along the way. The default handler simply displays 
the corresponding error message. To 

PAGE 23
 replace the default one with one more suitable for OpenGL, place this at the beginning of 
the FormCreate method:

  Application.OnException := ExceptionGL;	
Finally, here is the body of the new exception handler. You are supposed to call 
ShowException, but in this context it discards the intended error message and shows the 
name of the form as the error message. Instead, just call ShowMessage with the message in 
the exception object, then in the loop call glGetError and show all the pending OpenGL 
errors until they have all been cleared:

procedure TForm1.ExceptionGL(Sender:TObject;E:Exception);	
begin	
  ShowMessage(E.Message);	
  repeat	
    errorCode := glGetError;	
    if errorCode<>GL_NO_ERROR then	
      showMessage(gluErrorString(errorCode));	
  until errorCode=GL_NO_ERROR;	
end;	
Order of Events
Now, compile the program and launch it with the Windows Run command. Resize it several 
ways. Watch the shape of the rectangle as you do the resizing. Careful observation reveals 
that if the form only shrinks, the shape of the rectangle does not change. If the form enlarges 
either horizontally or vertically or both, the shape of the rectangle changes appropriately. An 
expected event is not firing. This calls for further investigation.
In the variable declaration section add:

    EventFile: TextFile;	
So that an “Event.Log” text file opens for writing at program start and closes at program 
end, make the end of the unit look like this:

initialization	
  assignFile(EventFile,'Event.Log');	
  rewrite(EventFile);	
finalization	
  closeFile(EventFile);	
end.	
To record each firing of the OnPaint event, add the following at the beginning of FormPaint:

  writeln(EventFile,'Paint  '+IntToStr(TimeGetTime));	

PAGE 24
 TimeGetTime is a Windows API call that returns the number of milliseconds since Windows 
last loaded. To access it the unit needs MMSystem in the uses clause. Now, to log each firing 
of the OnResize event, at the beginning of FormResize add this line:

  writeln(EventFile,'Resize '+IntToStr(TimeGetTime));	
Now compile and run as before. Wait about one second. Shrink the form from the right side. 
Wait another second. Shrink the form from the bottom. Wait another second. Enlarge the 
form a little at the bottom. Now close the form. Examine the contents of the Event.Log file in 
the current (project) directory. At the top of the file are “Resize” and “Paint” with nearly 
identical time stamps, only 10 or 20 milliseconds apart. They fired at program start. About 
1,000 milliseconds later (depending on the accuracy of the “one-second” wait) is another 
“Resize” line. That fired when the form shrank from the right side. Another 1,000 
milliseconds later is another “Resize” entry, which fired when the form shrank from the 
bottom. Still 1,000 millisconds later is another “Resize” followed shortly by a “Paint.”
After careful consideration, this makes sense. When the form shrinks, Windows only sends a 
Resize message. Why should Windows send a Paint message just for the covering of some 
already painted territory? Only form enlargement in one or both directions exposes new area 
that needs painting. Windows does not know about the special need to adjust the interior of 
the form. Since the Windows Resize and Paint messages are what trigger the Delphi 
OnResize and OnPaint events, a form shrinkage does not trigger the necessary adjustment of 
the rectangle.
The way to remedy this little problem is to call FormPaint on those occasions when it would 
otherwise be neglected. FormResize is called for every OnResize event, so that is a good 
place to check whether either dimension enlarged. If not, then no OnPaint event happens, so 
you must call FormPaint directly. After cleaning out the text file code, add these fields to the 
private section of the form type declaration:

    oldw,	
    oldh: integer;	
The fields preserve the previous width and height for comparison with new values. Initialize 
them at the end of the FormCreate method:

  oldw := ClientWidth;	
  oldh := ClientHeight;	
Finally, this code at the end of FormResize ensures the FormPaint method always fires when 
needed:


PAGE 25
   if (ClientWidth<=oldw) and (ClientHeight<=oldh) then	
    FormPaint(Sender);	
  oldh := ClientHeight;	
  oldw := ClientWidth;	
Save, compile, and run (from Windows). Resize to your heart’s content.
Matrices and Command Placement
At this point the program is ready for a little experimentation with glOrtho. Change left 
from –1.0 to 0.0, then save, compile, and run as before.

  glOrtho(0.0,1.0,-1.0,1.0,-1.0,1.0);	
Remember that the values given to the vertices of the rectangle are relative to the clipping 
volume. Since the range of horizontal numbers in the call to glOrtho is half the former range, 
the rectangle should be twice as wide as before. Program execution shows that to be the case. 
Now, resize the form exactly one time. Something is wrong! The rectangle jumps to the left 
side. Resize the form again. The rectangle is gone, never to be seen again!
This experience exposes another important concept. OpenGL relies heavily on matrices. A 
matrix is a two-dimensional array of numbers for which a special set of mathematical 
operations applies. For every call to glOrtho, OpenGL creates a matrix from the parameters 
and multiplies it by the projection matrix. Each successive call changes the appropriate 
matrix. The symmetrical numbers passed to glOrtho just masked the potential strange 
behavior, but the unbalanced numbers for this version exposed the problem. One solution is 
to move the call to glOrtho to the last line of the FormCreate method. Save, compile, and run. 
Resize the form a few times. Now the expected behavior manifests itself.
Go ahead and experiment some more with passing different values to glOrtho, leaving 
everything else the same. These variations should give a sense of the relationship between 
the values in the projection matrix and the values of the vertices.
Viewport Manipulation
The viewport is the next experimental subject. The orthographic projection can change some 
of its boundary numbers but still have the same boundary (the viewport). Just the arbitrary 
meaning of the numbers change. The viewport boundary numbers have a real meaning, 
which is window coordinates. Changing these numbers changes the actual boundary of the 
viewport.

PAGE 26
 Restore the call to glOrtho to its symmetrical arrangement, but leave it at the bottom of 
FormCreate.

  glOrtho(-1.0,1.0,-1.0,1.0,-1.0,1.0);	
end; {FormCreate}	
In FormResize, change the vertical extent in the call to glViewport.

  glViewport(0,0,ClientWidth,ClientHeight div 2);	
Compile and run as before. From this point on, using the Windows Run command rather 
than running from the IDE is taken for granted and needs no further mention. Now the 
viewport only covers the lower half of the window. Remember that OpenGL coordinates 
normally start from the bottom of the window, even when using “window coordinates.” The 
red rectangle is still in the upper right quadrant of the viewport, but since the viewport itself 
is confined to the lower half of the window, the rectangle is now in the lower half of the 
window. Since the viewport is only half as tall, the rectangle is half as tall as it was.
Now, try it the other way.

  glViewport(0,0,ClientWidth div 2,ClientHeight);	
Compile and run. This time everything lies on the left side of the screen. The rectangle is still 
in the right side of the viewport, but it lies in the left side of the window. Notice that with 
these restrictions on the size of the viewport, the background color still covers the whole 
window. This experiment should be enough to give a good feel for glViewport, so take out 
the div operators to restore the original appearance.
Mixing Windows Components
One of the really neat features of OpenGL is that it does not prevent using regular Windows 
components. This opens many possibilities for user interaction with your graphics 
constructs. The coming chapters will explore a number of different ways users can give 
information to your applications. Since these are illustrations and exercises, they are not the 
ultimate in slick, state-of-the-art front ends; they are just enough to demonstrate a technique. 
For the first such demonstration, drop a MainMenu component on the form. Right-click the 
component and select the Menu Designer. Give it one menu item with a caption of &Z-
Value. Close the Menu Designer, click the menu item, and edit the event handler as follows:

procedure TForm1.Zvalue1Click(Sender: TObject);	
var	
  zString: string[7];	
begin	


PAGE 27
   zString := InputBox('Z-Value','float:','0.0');	
end;	
The first argument gives the title of the input box. The second is the prompt. “float:” serves 
as a reminder to enter a floating-point value. The third argument provides a default value to 
return.
Position the form near the center of the screen or set the form’s Position property to 
poScreenCenter. Save, compile, and run. Click the menu item, and close the input box. Both 
the menu and the input box showed properly. Since the form shows at or near the center of 
the screen, and the input box shows near the center of the screen, the input box has to 
obscure part of the form. Since there was no hole left in the form when the input box closed, 
then the OnPaint event must have fired. The only adverse effect is the fact that the viewport 
is a little shorter, since the presence of the menu reduced the ClientHeight.
Pushing the (X,Y,Z) Limits
Add zvalue as a new field to the private section of the form declaration:

  private	
    GLContext: HGLRC;	
    glDC: HDC;	
    errorCode: GLenum;	
    openGLReady: boolean;	
    oldw,	
    oldh: integer;	
    zvalue: GLfloat;	
    procedure ExceptionGL(Sender:TObject;E:Exception);	
Edit the Zvalue1Click event handler:

procedure TForm1.Zvalue1Click(Sender: TObject);	
var	
  zString: string[7];	
begin	
  zString := InputBox('Z-Value','float:','0.0');	
  zvalue := StrToFloat(zString);	
end;	
Change the calls to glVertex from the two-parameter version to the three-parameter version 
inside FormPaint, using the freshly minted zvalue as the third argument:

  glBegin(GL_POLYGON);	
    glVertex3f(0.25,0.25,zvalue);	
    glVertex3f(0.75,0.25,zvalue);	
    glVertex3f(0.75,0.75,zvalue);	


PAGE 28
     glVertex3f(0.25,0.75,zvalue);	
  glEnd;	
Save, compile, and run. Select the menu item and enter 0.5 in the input box (and press the 
OK button, of course). Do it again, but enter 1.0 as the value. Again, but use 1.01. Something 
different happened! The rectangle disappeared! Every time the input box covers part of the 
form and closes, the form must repaint. The results of each new zvalue appear as soon as the 
box closes. As long as zvalue is between the z-axis limits of the clipping volume (the glOrtho 
projection), the rectangle is visible. The side of the clipping volume toward the viewer is at z 
= 1.0. The side of the clipping volume away from the viewer, into the screen, is at z = –1.0. As 
long as the rectangle lies between –1.0 and 1.0 on the z-axis, it is visible. Values of 0.5, 1.0, –
0.5, and –1.0 kept the figure in sight, but 1.01 and –1.01 made it vanish.
Use the Menu Designer to add two more main menu items. The captions are &Y-Value and 
&X-Value. Add the following event handlers:

procedure TForm1.YValue1Click(Sender: TObject);	
var	
  yString: string[7];	
begin	
  yString := InputBox('Y-Value','float:','0.0');	
  yvalue := StrToFloat(yString);	
end;	
	
procedure TForm1.XValue1Click(Sender: TObject);	
var	
  xString: string[7];	
begin	
  xString := InputBox('X-Value','float:','0.0');	
  xvalue := StrToFloat(xString);	
end;	
Add xvalue and yvalue to the private section of the form declaration:

  private	
    GLContext: HGLRC;	
    glDC: HDC;	
    errorCode: GLenum;	
    openGLReady: boolean;	
    oldw,	
    oldh: integer;	
    xvalue,	
    yvalue,	
    zvalue: GLfloat;	
    procedure ExceptionGL(Sender:TObject;E:Exception);	

PAGE 29
 Use the new fields in the drawing:

  glBegin(GL_POLYGON);	
    glVertex3f(xvalue+0.25,yvalue+0.25,zvalue);	
    glVertex3f(xvalue+0.75,yvalue+0.25,zvalue);	
    glVertex3f(xvalue+0.75,yvalue+0.75,zvalue);	
    glVertex3f(xvalue+0.25,yvalue+0.75,zvalue);	
  glEnd;	
Save, compile, and run. Try 0.5 and –1.5 for yvalue, then go back to 0.0. Try the same 
numbers for xvalue. Now the meaning of a clipping volume should be quite clear. If any part 
of the figure goes outside the boundaries of the clipping volume, that part is clipped off, but 
the rest remains visible.
Naturally, good code uses a more reliable method of ensuring a repaint than just having an 
input box go away, but for this little clipping demonstration, it was sufficient.

PAGE 30
 
Identifier Reference
Identifier			Description
glBegin
An OpenGL command designating the start of a list of vertices. It receives a mode parameter 
that tells OpenGL what to do with those vertices.
GLbitfield
An OpenGL type that maps to Delphi cardinal.
GLclampf
An OpenGL type equivalent to Delphi single.
glColor
An OpenGL command that sets the foreground color.
GLdouble
An OpenGL type equivalent to Delphi double.
glEnd
An OpenGL command that terminates a list of vertices started with glBegin.
glFlush
An OpenGL command that forces drawing to the screen.
GLint
An OpenGL type that maps to Delphi integer.
glLineWidth
An OpenGL command that specifies the width, in pixels, of all subsequent lines drawn.
glOrtho
An OpenGL command that specifies an orthographic projection and clipping volume.
glRect
An OpenGL command for drawing a rectangle.
GLsizei
An OpenGL type that maps to Delphi integer.
glVertex
An OpenGL command for specifying the coordinates of a vertex.
glViewport
An OpenGL command that specifies the viewport in window coordinates.
GL_COLOR_BUFFER_BIT
An OpenGL constant that identifies the color buffer.
GL_LINES
An OpenGL constant to pass to glBegin. Each pair of vertices then specifies a line segment, 
separate from any of the other line segments.
GL_POLYGON
An OpenGL constant to pass to glBegin. All the vertices listed before glEnd are connected to 
form a polygon.

PAGE 31
 Identifier			Description
TimeGetTime
A Windows function that returns the number of milliseconds since Windows was started.
Summary
Chapter 2 presented the following concepts:
? Drawing is performed in the window’s OnPaint event handler.
? Drawing occurs within the bounds of the viewport.
? The viewport definition uses the operating system’s window coordinates, but counts in 
the y-direction from the bottom up.
? Use glColor to set the foreground drawing color, changing as needed. The color is one of 
many state variables that remain the same until the programmer directly changes them via 
function or procedure call.
? Presenting a 3-D scene on a 2-D screen requires projection.
? Orthographic projection does not change the apparent size of an object to correct for 
distance. It is also called parallel projection.
? The projection is a volume in space bounded by the viewport.
? The programmer may assign the numbers defining the projection volume for clarity or 
convenience.
? The projection region is a clipping volume. Any part of a scene that lies outside the 
volume simply does not appear.
? Adjust the viewport in the window’s OnResize event handler.
? Projection involves matrix multiplication. Repeated invocation is repeated multiplication 
which probably gives unintended results. The solution in this chapter was to invoke 
projection only once in the FormCreate method.
? OpenGL does not hinder the use of Windows features such as menus and dialog boxes.
? The glGetError function returns the error code number for recent operations. It returns a 
number equal to GL_NO_ERROR if no errors have accumulated. OpenGL stores 
accumulated errors, so the program must call glGetError repeatedly until GL_NO_ERROR 
comes back.

PAGE 32

