| Drawing LinesLast time we discussed the basic elements of polygon construction under OpenGL. 
          OpenGL only supports a few basic primitive geometrical objects: points, lines, 
          polygons, and surfaces described by arrays of small quadrilaterals or triangles. The main idea behind OpenGL's simplicity is that it is up to the developer to 
          implement from this simple objects more complex geometrical models. OpenGL contains 
	    a number of commands to control the details of points, lines and polygons. For example the size of points can be specified in pixels using  
          glPointSize: void glPointSize(GLfloat size)
 
 
 By the default the size of points is 1.0 and size must always be  
          greater than zero. Notice that the size of a point is specified by a float number; 
          fractional point and line sizes are allowed. OpenGL interprets fractional pixel 
          sizes according to the rendering context. If the anti-aliasing mode is enable then 
          OpenGL modifies the neighboring pixels to the line in question in order to 
	  give the appearance of a fractional width. Anti-aliasing is a technique also 
          used to eliminate the ugly stars the straight lines show on computer screens 
          at low monitor resolution. If anti-aliasing is not enable then glPointSize 
          will round off size to the closets integer.  The physical size of a pixel actually is device dependent. So for example 
	  at low monitor resolution a pixel appears wider. Similarly on very high resolution 
	  devices, like a plotter, the default 1 pixel line can appear almost invisible. 
          To estimate the real width of your lines you must know the actual physical  
          dimensions of pixels on the output device. The width of lines is specified by the glLineWidth function, that 
          must be invoked before the glBegin() - glEnd() pair that draws the line(s). 
          Here is the full syntax of the command: void glLineWidth(GLfloat width)
 
 
 OpenGL implementations may limit the width of  nonantialiased lines
	  to its maximum antialiased line width, rounded to the nearest integer value.
          Also keep in mind that line widths are measured not perpendicularly to the line but
          in the y-direction if the absolute value of the slope is less than 1; if greater
	  in the x-direction.   This month we have prepared another simple but hopefully useful 2D animation
	  that shows you how to use various kinds of line widths in your OpenGL applications
        (../../common/March1998/example2.c, ../../common/March1998/Makefile).
        I chose an example from Quantum Physics, a quantum particle trapped in a double 
	  well potential. Why? Umm actually I forget. Anyway I figure that it would be
        useful for physics and engineering students to see how to integrate the time
	  dependent Schroedinger equation, others may just enjoy watching the non-intuitive
	  nature of quantum mechanics. A particle in QM is not represented by a position and
	  a velocity but by a "wave" a quantum wave (solid purple line in our animation)
	  whose absolute square value represents the probability of observing the particle
	  at a given position (dash white line): ![[Click here to see the image]](../../common/March1998/ogl-thumb.gif)  Figure 1. Quantum Simulation Snapshot
 For those with some course work in Ordinary Differential Equations I can tell you
	 that the wave equation is integrated using a FFT (Fast Fourier Transform) Split-Operator
       method. This method is far more accurate and rapid than any finite difference method.
       It is applicable to nonlinear wave propagation; the time evolution operator is split
	 to second (or higher order) into operators only dependent on either position and
	 momentum (frequency), then the wavefunction is evolved in time by successively applying
       these operators switching back and forth between the position and momentum (frequency)
       space. The body of the source code can be used for many other applications. You can swap my
       quantum simulation with your own time dependent function and get a nice animation
       of your system. You could also try to write a simplified OpenGL-based gnuplot for
	 plotting functions and data files. If the reader has followed the previous articles on GLUT and OpenGL this source
        code will appear too simple and easy to understand (of course, quantum mechanics aside).
	  There is nothing extraordinary going here. In the main() we open a single
        window in double-buffer mode, then we pass a display() and idle()
	  callback functions that take care of plotting the wavefunction and integrating the
        wave equation respectively. Again, do mind what goes on in the idle() function,
        although it is a very beautiful trick is not necessary to fully understand it 
	  to grasp the content of this article. The really new OpenGL stuff is in the
        display callback function: 
void
display (void)
{
  static char label[100];
  float xtmp;
  /* Clean drawing board */
  glClear (GL_COLOR_BUFFER_BIT);
  /* Write Footnote */
  glColor3f (0.0F, 1.0F, 1.0F);
  sprintf (label, "(c)Miguel Angel Sepulveda 1998");
  glRasterPos2f (-1.1, -1.1);
  drawString (label);
  /* Draw fine grid */
  glLineWidth (0.5);
  glColor3f (0.5F, 0.5F, 0.5F);
  glBegin (GL_LINES);
  for (xtmp = -1.0F; xtmp < 1.0F; xtmp += 0.05)
    {
      glVertex2f (xtmp, -1.0);
      glVertex2f (xtmp, 1.0);
      glVertex2f (-1.0, xtmp);
      glVertex2f (1.0, xtmp);
    };
  glEnd ();
  /* Draw Outsite box */
  glColor3f (0.1F, 0.80F, 0.1F);
  glLineWidth (3);
  glBegin (GL_LINE_LOOP);
  glVertex2f (-1.0F, -1.0F);
  glVertex2f (1.0F, -1.0F);
  glVertex2f (1.0F, 1.0F);
  glVertex2f (-1.0F, 1.0F);
  glEnd ();
  /* Draw Grid */
  glLineWidth (1);
  glColor3f (1.0F, 1.0F, 1.0F);
  glBegin (GL_LINES);
  for (xtmp = -0.5; xtmp < 1.0; xtmp += 0.50)
    {
      glVertex2f (xtmp, -1.0);
      glVertex2f (xtmp, 1.0);
      glVertex2f (-1.0, xtmp);
      glVertex2f (1.0, xtmp);
    };
  glEnd ();
  /* Draw Coordinate Axis */
  glLineWidth (2);
  glBegin (GL_LINES);
  glVertex2f (-1.0, 0.0);
  glVertex2f (1.0, 0.0);
  glVertex2f (0.0, -1.0);
  glVertex2f (0.0, 1.0);
  glEnd ();
  /* Axis Labels */
  glColor3f (1.0F, 1.0F, 1.0F);
  sprintf (label, "Position");
  glRasterPos2f (0.80F, 0.025F);
  drawString (label);
  glColor3f (1.0F, 0.0F, 1.0F);
  sprintf (label, " Quantum Probability ");
  glRasterPos2f (0.025F, 0.90F);
  drawString (label);
  glColor3f (1.0F, 1.0F, 1.0F);
  sprintf (label, " Real(Psi) ");
  glRasterPos2f (0.025F, 0.85F);
  drawString (label);
  /* Draw Wavefunction */
  psiDraw (NR_POINTS, psi, x);
  /* Draw potential Function */
  potentialDraw (NR_POINTS, potential, x);
  glutSwapBuffers ();
};
The first thing done is to clear the color buffer bit, this gives us a clean (black)
        drawing board. Then we add a footnote using glRasterPos and 
        glutBitmapCharacter (drawstring is nothing but a wrapper for the clut utility).
        In future lessons glRasterPos  will appear again as an auxiliary function
        for texture rendering. Neither OpenGL nor GLUT offer a simple and powerful way for
	  rendering text onto a graphic window. The glutBitmapCharacter basically rasters a
        font bitmap onto the color buffer.  Following the footnote comes a number of lines: the outside box, the background grid,
        the coordinate axis, and of course the current curves drawn with psiDraw and
        potentialDraw. Before every line rendered is a glLineWidth that 
        specifies the number of pixels of width to be given to the line. Figure 1 shows 
        the output on an Xwindow System (Linux Alpha). For some unknown reason to me the
	  Windows 95 output of the same program looks very crappy, it appears as if the antialiasing
        feature is not well supported by the SGI OpenGL driver; it is hard ti differentiate lines
        that in principle should have different widths, and the background grid of lines 
        also appears very uniform. These defects appear when the display is set at high resolution
        so it is not an artifact of a low resolution monitor setup. I am happy to say that
        Linux X window system defeats by large win95/NT once more. There are two types of line rendering in the display() function, 
        GL_LINES mode which  joins vertices with a continuous open line and GL_LINE_LOOP mode 
        that at the end closes the loop.  Antialiasing LinesI have enabled antialiasing for the lines in the reshape() callback function, 
void
reshape (int w, int h)
{
  glMatrixMode (GL_MODELVIEW);
  glLoadIdentity ();
  glViewport (0, 0, w, h);
  glMatrixMode (GL_PROJECTION);
  glLoadIdentity ();
  gluOrtho2D (-1.2, 1.2, -1.2, 1.2);
  glEnable (GL_LINE_SMOOTH);     /* Enable Antialiased lines */
  glEnable (GL_LINE_STIPPLE);
};
 What is GL_LINE_STIPPLE for? OpenGL let us control not only the width of a
       line but also its pattern. By enabling GL_LINE_STIPPLE we are able to draw
       dash or any other pattern of lines. The only stippled line in the animation appears
       in the psiDraw() function: 
  glLineWidth (1);
  glPushAttrib (GL_LINE_BIT);
  glLineStipple (3, 0xAAAA);
  glBegin (GL_LINE_STRIP);
  for (i = 0; i < nx; i++)
    {
      xs = ratio1 * (x[i] - XMIN) - 1.0;
      ys = ratio2 * (psi[2 * i] - YMIN) - 1.0;
      glVertex2d (xs, ys);
    };
  glEnd ();
  glPopAttrib ();
Line StipplingThe glLineStipple specifies the pattern used for stippling, in our 
        example we used the pattern 0xAAAA. In binary this numbers read as 
        0000100010001000 and OpenGL interprets this drawing 3 bits off, 1 bit on, 3 bits off,
        1 bit on, 3 bits off,  1 bit on and finally 4 bits off. Yes the pattern is read 
        backwards because the low order bits are used first. Now glLineStipple 
        gets two parameters, the stippled pattern which should be an hexadecimal number and
	  and integer factor which serve to scale the pattern, so with a factor of 3
        our stippled line will show 9 bits off, 3 bits on, 9 bits off, 3 bits on, 
        9 bits off, 3 bits on and finally 12 bits off. Playing with factors and binary
        patterns one can draw all sort of complicated stippled lines. One more detail: I have enclosed the stippled line rendering between a push
         and pop Attribute statement. Remeber when in our first article we mentioned that
         OpenGL is a state machine? Well in future articles we will see in more detail
         these push and pop operations, but in short what we are doing with the first
         glPushAttrib (GL_LINE_BIT) is to push in a stack the current value of the
         GL_LINE_BIT state variable (this variables decides the stippling pattern), then
	   we can alter GL_LINE_BIT with our glLineStipple statement and when we
         are done we call a glPopAttrib that brings back the old GL_LINE_BIT
         variable. This mechanism is an effective way to modified the state variables of
         the OpenGL machine locally. If we didn't do this then all lines draw after
         our glLineStipple would have the same stippling pattern and we would
         be forced to declare a  glLineStipple patter for every line we ever
         render in our application. Push & Pop saves us this annoying work. Next Time ....OpenGL is famous for is wonderful 3D API interface. So far we have explored
         some elemental possibilities of 2D rendering with OpenGL. Next time we will
         examine the 3D OpenGL scene, how to set a perspective, system of coordinates,
         clipping planes and projection matrixes. Till then have fun with OGL...... |