Kinesia Online Course
Computer Graphics
Kinesia LLC, 2003

    1. Introduction
    2. Line Drawing
    3. Drawing Objects
    4. More Drawing Tools
    5. Normal Vectors and Polygonal Models of Surfaces
    6. Viewing I -- Affine Transformations
    7. Viewing II -- Projections
    8. Color
    9. Lighting
    10. Blending, antialiasing, fog ..
    11. Display Lists, Bitmaps and Images
    12. Texture Mapping
    13. Curves and Surfaces
    14. Games and SDL

    Blending, Antialiasing, Fog and Polygon Offset

    We are what we repeatedly do.
    Excellence, then, is not an act, but a habit.
    Aristotle

    1. Transparency

    2. In normal rendering, we ignore the alpha value, the A in RGBA color.

    3. If we first enable blending, by

        glEnable( GL_BLEND );

      then the alpha value will have meaning.

        1 -- material opaque, hides everything behind

        0 -- material completely transparent and cannot be seen

    4. When we render only opaque polygons, the z-buffer for hidden surface removal is enough to render objects properly

    5. Blending model:

      • destination -- color buffer ( holds color info for display )

      • source -- polygon colors that we are working on

      • new destination color = combination of old destination color and source color


         Source Color 
         

        -----------→
             
        |
        |
        |
        |
        |
        |
        |
        |
        |
        |
        --------→
               
        ------------

         blending 
         
        |
        |
        |
        |
        |
        |
        |
        |

         Destination
         Color 
         

      • combine using source and destination factors

      • Suppose, at some point
          destination color = ( Rd, Gd, Bd, Ad )
          polygon color = ( Rs, Gs, Bs, As )
          destination factor = ( Dr, Dg, Db, Da )
          source factor = ( Sr, Sg, Sb, Sa )

        The blended color ( new destination color ) is

          ( RsSr + RdDr,   GsSg + GdDg,   BsSb + BdDb,   AsSa + AdDa )

          Each component is clamped to [0,1].

      • gl blending functions

        used to generate blending factors

        void glBlendFunc(GLenum sfactor, GLenum dfactor);
        Controls how color values in the fragment being processed (the source) are combined with those already stored in the framebuffer (the destination). The argument sfactor indicates how to compute a source blending factor; dfactor indicates how to compute a destination blending factor. The possible values for these arguments are explained in Table below. The blend factors are assumed to lie in the range [0,1]; after the color values in the source and destination are combined, they're clamped to the range [0,1].

        void glBlendFuncSeparate(GLenum srcRGB, GLenum destRGB, GLenum srcalpha, GLenum destalpha );
        Still use the table below but blending factors of RGB and A are considered separately.

        Constant

        Relevant Factor

        Computed Blend Factor

        GL_ZERO

        source or destination

        (0, 0, 0, 0)

        GL_ONE

        source or destination

        (1, 1, 1, 1)

        GL_DST_COLOR

        destination

        (Rd, Gd, Bd, Ad)

        GL_SRC_COLOR

        source

        (Rs, Gs, Bs, As)

        GL_ONE_MINUS_DST_COLOR

        source

        (1, 1, 1, 1)-(Rd, Gd, Bd, Ad)

        GL_ONE_MINUS_SRC_COLOR

        destination

        (1, 1, 1, 1)-(Rs, Gs, Bs, As)

        GL_SRC_ALPHA

        source or destination

        (As, As, As, As)

        GL_ONE_MINUS_SRC_ALPHA

        source or destination

        (1, 1, 1, 1)-(As, As, As, As)

        GL_DST_ALPHA

        source or destination

        (Ad, Ad, Ad, Ad)

        GL_ONE_MINUS_DST_ALPHA

        source or destination

        (1, 1, 1, 1)-(Ad, Ad, Ad, Ad)

        GL_SRC_ALPHA_SATURATE

        source

        (f, f, f, 1); f=min(As, 1-Ad)

        GL_CONSTANT_COLOR

        source or destination

        ( Rc, Gc, Bc, Ac )

        GL_CONSTANT_ALPHA

        source or destination

        ( Ac, Ac, Ac, Ac )

        If you are using one of the GL*CONSTANT* blending functions, you need to use glBendColor() to specify the constant colors.

        void glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha );
        Sets the current red, green, blue and alpha values for use as the constant color ( Rc, Gc, Bc, Ac ) in blending operations.

        Example: When we draw polygonal surfaces, the most common choices for the source and destination factors are GL_SRC_ALPHA and ONE_MINUS_SRC_ALPHA respectively.
        Consider initially,

          color buffer = ( Rc, Gc, Bc, 1 )

          color of polygon to be blended = ( Rs, Gs, Bs, As )

        Class exercise: What will be the resulted rendered destination color D?

        Answer:

          D = ( AsRs + ( 1 - As)Rc,   AsGs + ( 1 - As)Gc,  
                AsBs + ( 1 - As)Bc,   As2 + ( 1 - As)Ac )
            = ( Rd, Gd, Bd, Ad )

        void glBlendEquation( GLenum mode );
        Specifies how framebuffer and source colors are blended together.
        GL_FUNC_ADD ( default ), GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT, GL_MIN, GL_MAX

    6. A Blending Example

      /*
       *  alpha.cpp
       *  This program draws several overlapping filled polygons
       *  to demonstrate the effect order has on alpha blending results.
       *  Use the 't' key to toggle the order of drawing polygons.
       */
      #include <GL/glut.h>
      #include <stdlib.h>
      
      static int leftFirst = GL_TRUE;
      
      /*  Initialize alpha blending function.
       */
      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);
      }
      
      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();
      }
      
      static void drawRightTriangle(void)
      {
         /* draw cyan triangle on RHS of screen */
      
         glBegin (GL_TRIANGLES);
            glColor4f(0.0, 1.0, 1.0, 0.75);
            glVertex3f(0.9, 0.9, 0.0); 
            glVertex3f(0.3, 0.5, 0.0); 
            glVertex3f(0.9, 0.1, 0.0); 
         glEnd();
      }
      
      void display(void)
      {
         glClear(GL_COLOR_BUFFER_BIT);
      
         if (leftFirst) {
            drawLeftTriangle();
            drawRightTriangle();
         }
         else {
            drawRightTriangle();
            drawLeftTriangle();
         }
      
         glFlush();
      }
      
      void reshape(int w, int h)
      {
         glViewport(0, 0, (GLsizei) w, (GLsizei) h);
         glMatrixMode(GL_PROJECTION);
         glLoadIdentity();
         if (w <= h) 
            gluOrtho2D (0.0, 1.0, 0.0, 1.0*(GLfloat)h/(GLfloat)w);
         else 
            gluOrtho2D (0.0, 1.0*(GLfloat)w/(GLfloat)h, 0.0, 1.0);
      }
      
      void keyboard(unsigned char key, int x, int y)
      {
         switch (key) {
            case 't':
            case 'T':
               leftFirst = !leftFirst;
               glutPostRedisplay();	
               break;
            case 27:  /*  Escape key  */
               exit(0);
               break;
            default:
               break;
         }
      }
      
      /*  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_SINGLE | GLUT_RGB);
         glutInitWindowSize (200, 200);
         glutCreateWindow (argv[0]);
         init();
         glutReshapeFunc (reshape);
         glutKeyboardFunc (keyboard);
         glutDisplayFunc (display);
         glutMainLoop();
         return 0;
      }
      	





      Draw yellow triangle first.




      Draw blue triangle first.

      blending factors are 0.75 ( source ) and 1 - 0.75 = 0.25 ( destination ),
      because blending factor of source is larger, it has more effect on the final color than do the destination color

    7. Three-Dimensional Blending with the Depth Buffer

      When drawing three-dimensional translucent objects, you can get different appearances depending on whether you draw the polygons from back to front or from front to back.

      To simplify rendering process, we enable depth buffering but make the depth buffer read-only ( controlled by glDepthMask() while drawing the translucent objects:

      • First draw all opaque objects, with depth buffer in normal operation,
      • Preserve the depth values by making depth buffer read-only,
      • Translucent objects are drawn only if they are in front of the opaque ones, and are blended with the opaque ones.

      /*
       *  alpha3D.c
       *  This program demonstrates how to intermix opaque and
       *  alpha blended polygons in the same scene, by using 
       *  glDepthMask.  Press the 'a' key to animate moving the 
       *  transparent object through the opaque object.  Press 
       *  the 'r' key to reset the scene.
       */
      #include <GL/glut.h>
      #include <stdlib.h>
      #include <stdio.h>
      #include <unistd.h>
      
      #define MAXZ 8.0	//maximum Z buffer value
      #define MINZ -8.0	//minimum Z 
      #define ZINC 0.4	//Z increment
      
      static float solidZ = MAXZ;
      static float transparentZ = MINZ;
      static GLuint sphereList, cubeList;
      
      static void init(void)
      {
         GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 0.15 };
         GLfloat mat_shininess[] = { 100.0 };
         GLfloat position[] = { 0.5, 0.5, 1.0, 0.0 };
      
         glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
         glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
         glLightfv(GL_LIGHT0, GL_POSITION, position);
      
         glEnable(GL_LIGHTING);
         glEnable(GL_LIGHT0);
         glEnable(GL_DEPTH_TEST);
      
         sphereList = glGenLists(1);
         glNewList(sphereList, GL_COMPILE);
            glutSolidSphere (0.4, 80, 80);
         glEndList();
      
         cubeList = glGenLists(1);
         glNewList(cubeList, GL_COMPILE);
            glutSolidCube (0.6);
         glEndList();
      }
      
      void display(void)
      {
         GLfloat mat_solid[] = { 0.75, 0.75, 0.0, 1.0 };
         GLfloat mat_zero[] = { 0.0, 0.0, 0.0, 1.0 };
         GLfloat mat_transparent[] = { 0.0, 0.8, 0.8, 0.6 };
         GLfloat mat_emission[] = { 0.0, 0.3, 0.3, 0.6 };
      
         glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      
         glPushMatrix ();
            glTranslatef (-0.15, -0.15, solidZ);
            glMaterialfv(GL_FRONT, GL_EMISSION, mat_zero);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_solid);
            glCallList (sphereList);
         glPopMatrix ();
      
         glPushMatrix ();
            glTranslatef (0.15, 0.15, transparentZ);
            glRotatef (15.0, 1.0, 1.0, 0.0);
            glRotatef (30.0, 0.0, 1.0, 0.0);
            glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_transparent);
            glEnable (GL_BLEND);
            glDepthMask (GL_FALSE);	//read-only
            glBlendFunc (GL_SRC_ALPHA, GL_ONE);
            glCallList (cubeList);
            glDepthMask (GL_TRUE);	//normal read-write
            glDisable (GL_BLEND);	
         glPopMatrix ();
      
         glutSwapBuffers();
      }
      
      void reshape(int w, int h)
      {
         glViewport(0, 0, (GLint) w, (GLint) h);
         glMatrixMode(GL_PROJECTION);
         glLoadIdentity();
         if (w <= h)
            glOrtho (-1.5, 1.5, -1.5*(GLfloat)h/(GLfloat)w,
               1.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
         else
            glOrtho (-1.5*(GLfloat)w/(GLfloat)h,
               1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);
         glMatrixMode(GL_MODELVIEW);
         glLoadIdentity();
      }
      
      void animate(void)
      {
         if (solidZ <= MINZ || transparentZ >= MAXZ)
            glutIdleFunc(NULL);  	//disable execution of animate
         else {
            solidZ -= ZINC;
            transparentZ += ZINC;
            glutPostRedisplay();
            sleep ( 2 );		//sleep 2 seconds
         }
      }
      
      void keyboard(unsigned char key, int x, int y)
      {
         switch (key) {
            case 'a':
            case 'A':
               solidZ = MAXZ;
               transparentZ = MINZ;
               glutIdleFunc(animate);	//if no other pending events, 
      				//  execute animate 
               break;
            case 'r':
            case 'R':
               solidZ = MAXZ;
               transparentZ = MINZ;
               glutPostRedisplay();
               break;
            case 27:
              exit(0);
          }
      }
      
      int main(int argc, char** argv)
      {
         glutInit(&argc, argv);
         glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
         glutInitWindowSize(500, 500);
         glutCreateWindow(argv[0]);
         init();
         glutReshapeFunc(reshape);
         glutKeyboardFunc(keyboard);
         glutDisplayFunc(display);
         glutMainLoop();
         return 0;
      }
        

       

       

    8. Antialiasing

      aliasing -- the jaggedness that appears when approximating an ideal line by a series of pixels that must lie on the pixel grid.

         

      To turn on antialiasing

      glEnable( GL_POINT_SMOOTH );
      Or
      glEnable( GL_LINE_SMOOTH );

      Use glHint() to provide a quality hint:

      void glHint(GLenum target, GLenum hint);
      Controls certain aspects of OpenGL behavior. The target parameter indicates which behavior is to be controlled; its possible values are shown in Table below. The hint parameter can be GL_FASTEST to indicate that the most efficient option should be chosen, GL_NICEST to indicate the highest-quality option, or GL_DONT_CARE to indicate no preference. The interpretation of hints is implementation-dependent; an implementation can ignore them entirely.

      Parameter

      Meaning

      GL_POINT_SMOOTH_HINT, GL_LINE_SMOOTH_HINT, GL_POLYGON_SMOOTH_HINT

      Specify the desired sampling quality of points, lines, or polygons during antialiasing operations

      GL_FOG_HINT

      Specifies whether fog calculations are done per pixel (GL_NICEST) or per vertex (GL_FASTEST)

      GL_PERSPECTIVE_CORRECTION_HINT

      Specifies the desired quality of color and texture-coordinate interpolation

      Example: Antialiased Lines

      //aargb.cpp
      #include <GL/gl.h>
      #include <GL/glu.h>
      #include <GL/glut.h>
      #include <stdlib.h>
      #include <stdio.h>
      
      static float rotAngle = 0.;
      
      /*  Initialize antialiasing for RGBA mode, including alpha
       *  blending, hint, and line width.  Print out implementation
       *  specific info on line width granularity and width.
       */
      void init(void)
      {
         GLfloat values[2];
         glGetFloatv (GL_LINE_WIDTH_GRANULARITY, values);
         printf ("GL_LINE_WIDTH_GRANULARITY value is %3.1f\n",
            values[0]);
         glGetFloatv (GL_LINE_WIDTH_RANGE, values);
         printf ("GL_LINE_WIDTH_RANGE values are %3.1f %3.1f\n",
            values[0], values[1]);
      
         glEnable (GL_LINE_SMOOTH);
         glEnable (GL_BLEND);
         glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
         glHint (GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
         glLineWidth (1.5);
      
         glClearColor(0.0, 0.0, 0.0, 0.0);
      }
      
      /* Draw 2 diagonal lines to form an X */
      void display(void)
      {
         glClear(GL_COLOR_BUFFER_BIT);
      
         glColor3f (0.0, 1.0, 0.0);
         glPushMatrix();
         glRotatef(-rotAngle, 0.0, 0.0, 0.1);
         glBegin (GL_LINES);
            glVertex2f (-0.5, 0.5);
            glVertex2f (0.5, -0.5);
         glEnd ();
         glPopMatrix();
      
         glColor3f (0.0, 0.0, 1.0);
         glPushMatrix();
         glRotatef(rotAngle, 0.0, 0.0, 0.1);
         glBegin (GL_LINES);
            glVertex2f (0.5, 0.5);
            glVertex2f (-0.5, -0.5);
         glEnd ();
         glPopMatrix();
      
         glFlush();
      }
      void reshape(int w, int h)
      {
         glViewport(0, 0, (GLint) w, (GLint) h);
         glMatrixMode(GL_PROJECTION);
         glLoadIdentity();
         if (w <= h) 
            gluOrtho2D (-1.0, 1.0, 
               -1.0*(GLfloat)h/(GLfloat)w, 1.0*(GLfloat)h/(GLfloat)w);
         else 
            gluOrtho2D (-1.0*(GLfloat)w/(GLfloat)h, 
               1.0*(GLfloat)w/(GLfloat)h, -1.0, 1.0);
         glMatrixMode(GL_MODELVIEW);
         glLoadIdentity();
      }
      
      void keyboard(unsigned char key, int x, int y)
      {
         switch (key) {
            case `r':
            case `R':
               rotAngle += 20.;
               if (rotAngle >= 360.) rotAngle = 0.;
               glutPostRedisplay();   
               break;
            case 27:  /*  Escape Key  */
               exit(0);
               break;
            default:
               break;
          }
      }
      
      int main(int argc, char** argv)
      {
         glutInit(&argc, argv);
         glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
         glutInitWindowSize (200, 200);
         glutCreateWindow (argv[0]);
         init();
         glutReshapeFunc (reshape);
         glutKeyboardFunc (keyboard);
         glutDisplayFunc (display);
         glutMainLoop();
         return 0;
      }
      

    9. Fog

      You can use fog to make an entire image appear more natural by adding fog, which makes object fade into the distance.

      Using the fog effect on points and lines is also called depth-cuing.

      Enabled by

      glEnable( GL_FOG );

      Fog blends a fog color with an object's color using a blending factor f ( in [0,1] ) computed according to:

      f = e -(density . z )   ( GL_EXP )
      f = e -(density . z )2   ( GL_EXP2 )
      f =     end - z    
      end - start
        ( GL_LINEAR )

      where z = distance between viewpoint and object

      density, end, start are input parameters of glFog*()

      If Cf = color of fog, Ci = color of object, the final blended color is given by

      C = fCi + ( 1 - f )Cf

      void glFog{if}(GLenum pname, TYPE param);
      void glFog{if}v(GLenum pname, TYPE *params);
      Sets the parameters and function for calculating fog. If pname is GL_FOG_MODE, then param is either GL_EXP (the default), GL_EXP2, or GL_LINEAR to select one of the three fog factors. If pname is GL_FOG_DENSITY, GL_FOG_START, or GL_FOG_END, then param is (or points to, with the vector version of the command) a value for density, start, or end in the equations. (The default values are 1, 0, and 1, respectively.) In RGBA mode, pname can be GL_FOG_COLOR, in which case params points to four values that specify the fog's RGBA color values. The corresponding value for pname in color-index mode is GL_FOG_INDEX, for which param is a single value specifying the fog's color index.

      Example: Five fogged spheres

      //fog.cpp
      #include <GL/gl.h>
      #include <GL/glu.h>
      #include <math.h>
      #include <GL/glut.h>
      #include <stdlib.h>
      #include <stdio.h>
      
      static GLint fogMode;
      
      static void init(void)
      {
         GLfloat position[] = { 0.5, 0.5, 3.0, 0.0 };
      
         glEnable(GL_DEPTH_TEST);
      
         glLightfv(GL_LIGHT0, GL_POSITION, position);
         glEnable(GL_LIGHTING);
         glEnable(GL_LIGHT0);
         {
            GLfloat mat[3] = {0.1745, 0.01175, 0.01175};      
            glMaterialfv (GL_FRONT, GL_AMBIENT, mat);
            mat[0] = 0.61424; mat[1] = 0.04136; mat[2] = 0.04136;     
            glMaterialfv (GL_FRONT, GL_DIFFUSE, mat);
            mat[0] = 0.727811; mat[1] = 0.626959; mat[2] = 0.626959;
            glMaterialfv (GL_FRONT, GL_SPECULAR, mat);
            glMaterialf (GL_FRONT, GL_SHININESS, 0.6*128.0);
         }
      
         glEnable(GL_FOG);
         {
            GLfloat fogColor[4] = {0.5, 0.5, 0.5, 1.0};
      
            fogMode = GL_EXP;
            glFogi (GL_FOG_MODE, fogMode);
            glFogfv (GL_FOG_COLOR, fogColor);
            glFogf (GL_FOG_DENSITY, 0.35);
            glHint (GL_FOG_HINT, GL_DONT_CARE);
            glFogf (GL_FOG_START, 1.0);
            glFogf (GL_FOG_END, 5.0);
         }
         glClearColor(0.5, 0.5, 0.5, 1.0);  /* fog color */
      }
      
      static void renderSphere (GLfloat x, GLfloat y, GLfloat z)
      {
         glPushMatrix();
         glTranslatef (x, y, z);
         glutSolidSphere(0.4, 60, 60);
         glPopMatrix();
      }
      
      /* display() draws 5 spheres at different z positions.
       */
      void display(void)
      {
         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
         renderSphere (-2., -0.5, -1.0);
         renderSphere (-1., -0.5, -2.0);
         renderSphere (0., -0.5, -3.0);
         renderSphere (1., -0.5, -4.0);
         renderSphere (2., -0.5, -5.0);
         glFlush();
      }
      
      void reshape(int w, int h)
      {
         glViewport(0, 0, (GLsizei) w, (GLsizei) h);
         glMatrixMode(GL_PROJECTION);
         glLoadIdentity();
         if (w <= h)
            glOrtho (-2.5, 2.5, -2.5*(GLfloat)h/(GLfloat)w,
               2.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
         else
            glOrtho (-2.5*(GLfloat)w/(GLfloat)h,
               2.5*(GLfloat)w/(GLfloat)h, -2.5, 2.5, -10.0, 10.0);
         glMatrixMode(GL_MODELVIEW);
         glLoadIdentity ();
      }
      
      void keyboard(unsigned char key, int x, int y)
      {
         switch (key) {
            case `f':
            case `F':
               if (fogMode == GL_EXP) {
                  fogMode = GL_EXP2;
                  printf ("Fog mode is GL_EXP2\n");
               }
               else if (fogMode == GL_EXP2) {
                  fogMode = GL_LINEAR;
                  printf ("Fog mode is GL_LINEAR\n");
               }
               else if (fogMode == GL_LINEAR) {
                  fogMode = GL_EXP;
                  printf ("Fog mode is GL_EXP\n");
               }
               glFogi (GL_FOG_MODE, fogMode);
               glutPostRedisplay();
               break;
            case 27:
               exit(0);
               break;
            default:
               break;
         }
      }
      
      int main(int argc, char** argv)
      {
         glutInit(&argc, argv);
         glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
         glutInitWindowSize(500, 500);
         glutCreateWindow(argv[0]);
         init();
         glutReshapeFunc (reshape);
         glutKeyboardFunc (keyboard);
         glutDisplayFunc (display);
         glutMainLoop();
         return 0;
      }
      

      GL_EXP
      GL_EXP2
      GL_LINEAR

    10. Analytic Form for Curves and Surfaces

    11. Implicit form:

      F( x, y ) = 0

      Examples:
      Straight line:

        Ax + By + C = 0

        F(x, y ) = Ax + By + C

      Circle:

        x2 + y2 = R2

        F(x, y) = x2 + y2 - R2

      Parabola:

        y2 - 4ax = 0

        F(x, y) = y2 - 4ax

      F(x, y) is also called inside-out function because:

        F(x, y) = 0     for all ( x, y ) on the curve

        F(x, y) > 0     for all ( x, y ) outside the curve

        F(x, y) < 0     for all ( x, y ) inside the curve

    12. Analytic ( parametric ) form:

      based on the value of a parameter, e.g. time t

      Exmples:
      Straight line:

        x(t) = a + bt

        y(t) = c + dt

        P(t) = ( x(t), y(t) )

      Circle:

        x(t) = cos ( t )

        y(t) = sin ( t )

        P(t) = ( x(t), y(t) )

      Parabola:

        x(t) = at2

        y(t) = 2at

        P(t) = ( x(t), y(t) )

      For surfaces:

      Implicit:

        F(x, y, z ) = 0

        e.g. Sphere:

          x2 + y2 + z2 - R2 = 0
          F(x, y, z ) = x2 + y2 + z2 - R2

      Parametric:

        A point on the surface P(s, t) = [ x(s, t), y(s, t), z(s, t) ]

        e.g. Sphere:

          x( θ, φ ) = R sin φ cos θ
          y( θ, φ ) = R sin φ sin θ
          z( θ, φ ) = R cos φ

          P( θ, φ ) = [ x(θ, φ), y(θ, φ), z(θ, φ ]

      The normal is given by

        ∂P(s,t)
        ∂ s
        x ∂P(s,t)
        ∂ t

        where

        ∂P(s,t)
        ∂ s
        = [ ∂x(s,t)
        ∂ s
        , ∂y(s,t)
        ∂ s
        , ∂z(s,t)
        ∂ s
        ]

        ∂P(s,t)
        ∂ t
        = [ ∂x(s,t)
        ∂ t
        , ∂y(s,t)
        ∂ t
        , ∂z(s,t)
        ∂ t
        ]

      Example: P(s, t) = [s2, t3, 3 - st ]

      We have

        ∂P(s,t)
        ∂ s
        = [ 2s, 0, -t]

        ∂P(s,t)
        ∂ t
        = [ 0, 3t2, -s]

        normal N = ∂P(s,t)
        ∂ s
        x ∂P(s,t)
        ∂ t
        = [ 3t3, 2s2, 6st2 ]
         

      So, when s=1 and t=2, the corresponding point on the surface is (1, 8, 1), and the vector (-24, 2, 24) is perpendicular to the surface at that point. The length of this vector is 34, so the unit normal vector is (-24/34, 2/34, 24/34) = (-0.70588, 0.058823, 0.70588).
    13. Polygon Offset

      If you want to highlight the edges of a solid object, you might try to draw the object with polygon mode GL_FILL and then draw it again, but in a different color with polygon mode GL_LINE. However, because lines and filled polygons are not rasterized in exactly the same way, the depth values generated for pixels on a line are usually not the same as the depth values for a polygon edge, even between the same two vertices. The highlighting lines may fade in and out of the coincident polygons, which is sometimes called .stitching. and is visually unpleasant.

      The visual unpleasantness can be eliminated by using polygon offset, which adds an appropriate offset to force coincident z values apart to cleanly separate a polygon edge from its highlighting line.

      You can either add a positive offset to the solid object ( push it away from you) or a negative offset to the wireframe (pull it towards you).

      void glPolygonOffset(GLfloat factor, GLfloat units);
      When enabled, the depth value of each fragment is added to a calculated offset value. The offset is added before the depth test is performed and before the depth value is written into the depth buffer. The offset value o is calculated by:
        o = m * factor + r * units
      where m is the maximum depth slope of the polygon and r is the smallest value guaranteed to produce a resolvable difference in window coordinate depth values. The value r is an implementation-specific constant.

      depth of polygon slope m = max { | ∂P(s,t)
      ∂ s
      | , | ∂P(s,t)
      ∂ t
      | }

      For polygons that are parallel to the near and far clipping planes, m = 0 . For the polygons in your scene with m near zero, only a small, constant offset is needed. To create a small, constant offset, you can pass factor=0.0 and units=1.0 to glPolygonOffset().
      i.e. use glPolygonOffset( 0.0, 1.0 );

      For polygons that are at a great angle to the clipping planes, the depth slope can be significantly greater than zero, and a larger offset may be needed. Small, non-zero values for factor, such as 0.75 or 1.0, are probably enough to generate distinct depth values and eliminate the unpleasant visual artifacts.
      e.g.. use glPolygonOffset( 0.75, 1.0 );