CS409 / CS 809 Tutorial 1: OpenGL

Setting up an OpenGL Project

1.      Start Microsoft Visual Studio 2015.  Select File / New / Project….  Under the list on the left, choose Installed / Templates / Visual C++.  Then choose a Win32 Console Application.  Change the name to Tutorial1 or some suitable name and change the location to a hard drive located on the computer (typically C or D).  You will need to remember this location for steps 3 and 5.  Make sure the “Create folder for project” checkbox is not checked and that the “Add to source control” is not checked.  Click OK.

·         If the Chose Location window keeps popping up again, you aren’t allowed to save files at the location you chose.  Select a different location, such as the desktop.

·         Do not put your program on one of the external drives like H.  The connection is slow, and your program may not compile correctly.

2.      Click Next> or Application Settings (they go to the same place).  Check the “Empty Project” box.  Make sure the “Security development lifecycle check” is not checked.  Click Finish.

3.      Go to http://www2.cs.uregina.ca/~anima/409/Terms/201810/Tutorials/index.html and download main1.cpp, Sleep.h, and Sleep.cpp to your program directory.

4.      In Visual C++, go to Project/Add Existing Item… and select main1.cpp.

5.      Download FreeGLUT for Windows.  The official website has source code and a Linux makefile, which isn't much use on Windows.  Rather than search the internet for a binary (compiled) version, you can download it from the course website at http://www2.cs.uregina.ca/~anima/409/Terms/201810/Tutorials/freeglut.zip.  Extract the files to your project folder.

·         If you are using a Windows computer with 7-Zip (such as the CL 105 computers), the easiest way to do this is by right-clicking on the file in Windows Explorer and selecting 7-Zip / Extract Here.

6.      Ensure that Solution Explorer is visible (View / Solution Explorer). Double-click main1.cpp to open it.

7.      Go to Project / Add Existing Item… and add glut.h to your project.  This should stop Visual Studio from marking all the OpenGL functions as errors.

8.      Compile and run the original program (using the green triangle on the third line just before “Local Windows Debugger”).  Be patient.  Eventually, you should see 2 windows: a text console and a graphical window labeled “First OpenGL Program”.  There is a white polygon (square) in the middle of this second window.  Exit the windows.  Closing the text window will close the graphical window, and sometimes vice versa.

·         You may see a pop-up window that says "This project is out of date:".  If so, click "Yes".  This tells Visual Studio to compile your program before trying to run it.

Playing with the Rectangle

9.      In the display function, add a line before the glBegin line that says
  glColor3f(1.0f, 0.5f, 0.0f); // float, range [0.0f, 1.0f]
The three numbers are values from 0.0 to 1.0 for red, green, and blue color components.  Compile and run your program.  The square should now be orange.

10.  Repeat step 8 with each of the lines
     
glColor3d(1.0, 0.5, 0.0);    // double, range [0.0, 1.0]
     
glColor3ub(255, 128, 0);     // unsigned byte, range [0, 255]
The results should be the same.  Choose the one of the three
glColor* lines that you like and keep that one.

11.  Change the x component (first number) of the first and fourth glVertex2f commands to –0.3f.  The lines should now appear as:
       glVertex2f(-0.3f, -0.5f);
       glVertex2f( 0.5f, -0.5f);
       glVertex2f( 0.5f,  0.5f);
       glVertex2f(-0.3f,  0.5f);

Compile and run the program; the square should be replaced by a rectangle right of center.

12.  Repeat step 10 with the x component as 0.0f.  The rectangle grows smaller.

13.  Duplicate the first glVertex2f lines.  In the original (still first) line, change the x component to –0.5f and the y component to 0.0f.  The five lines should appear as:
       glVertex2f(-0.5f,  0.0f);
       glVertex2f( 0.0f, -0.5f);
       glVertex2f( 0.5f, -0.5f);
       glVertex2f( 0.5f,  0.5f);
       glVertex2f( 0.0f,  0.5f);
Compile and run the program.  The orange rectangle should now be a pentagon.

·         Note: the vertices of the pentagon are given in counterclockwise order; use this order for any kind of polygon.

14.  Duplicate everything from glColor3f to glEnd, inclusive, and place the copy after the original glEnd.  In the copy, change the glColor3* line to green and remove the first two glVertex2f lines.  Change the remaining lines to have coordinates (1.0f, 1.0f), (-1.0f, 1.0f), and (0.0f, -1.0f).  Compile and run the program.  There should be a big green triangle on the screen with just a small corner of the orange pentagon visible.

15.  Move the triangle display code (including its glColor3* line) above the pentagon display code (including its glColor3* line).  Compile and run the program.  The pentagon should be in front of the triangle.

Rotating the Pentagon

16.  Add a function called idle to your program above the display function:
void idle ()
{
  // tell OpenGL to redisplay the window
  glutPostRedisplay();
}
Add a prototype for the idle function immediately before the prototype for the display function.  A prototype looks like the first line of the function but with a semi-colon at the end:
void idle ();

·         Try to keep your function prototypes in the same order as the functions themselves.  It is less confusing in the long term.

17.  In the main function, add a call to glutIdleFunc immediately before the call to glutDisplayFunc.
  glutIdleFunc(idle);

18.   Compile and run your program.  It should appear the same but occupy all available CPU time on one processor.  You may need to close the text window to stop it.

·         If the X doesn't work (depends on your computer), the problem is that the program is too busy to notice your input.  The glutMainLoop function in main keeps calling functions as needed, and it always thinks there is another function to call.  Whenever idle runs, glutPostRedisplay is called, which makes glutMainLoop call display again.  Then, when display is finished, glutMainLoop calls idle again.  Thus, it keeps running display, idle, display, idle, display, idle… as fast as possible and never has time to do anything else.

19.  Create a global floating point variable named pentagonDegrees with an initial value of 0.0.  Place it after the prototype declarations, but before the main function.
float pentagonDegrees = 0.0f;
The angle will be increased every time OpenGL is idle.  In the idle function, before glutPostRedisplay, insert the following:
  pentagonDegrees += 2.0f;
  if(pentagonDegrees >= 360.0f)
       pentagonDegrees -= 360.0f;

20.  Add the line
  glMatrixMode(GL_MODELVIEW);
at the beginning of the display function.  This tells OpenGL that we want our matrix commands (below) to apply to the modelview matrix, which is the normal one and controls where things appear on the screen.

21.  Add the lines
  glPushMatrix();    // saves existing top matrix
       glRotatef(pentagonDegrees, 0.0f, 0.0f, 1.0f);

immediately before the code to draw the pentagon.  Indent the code to draw the pentagon by one tab.  After the code to draw the pentagon, add the line
  glPopMatrix(); // restores matrix from glPushMatrix

·         You can indent code by selecting in and typing [TAB].  To un-indent code, type [SHIFT] + [TAB].

22.  Compile and run the program.  The orange pentagon should rotate so fast that it looks like the rotation in each frame is random.

23.  We will slow the animation down by making OpenGL wait 1/100 of a second between displaying each frame.  Go to Project / Add Existing Item… and add Sleep.h and Sleep.cpp to your project.  At the top of your main1.cpp file, add
#include "Sleep.h"
In your idle function, before glutPostRedisplay, add the line
  sleep(0.01);  // wait for 0.01 seconds
Compile and run the program.  The orange pentagon should rotate a sensible speed and the X button in the corner should work again.

Animating the Triangle

24.  Create two new global floating point variables, named xPosition (with an initial value of 0.0) and xIncrement (with an initial value of 0.01). 
float xPosition  = 0.0f;
float xIncrement = 0.01f;
In the idle function, insert the following after the code for pentagonDegrees:
  xPosition += xIncrement;
  if(xPosition > 1.0f || xPosition < -1.0f)
       xIncrement = -xIncrement;

25.  Add the lines
  glPushMatrix();
       glTranslatef(0.0f, 0.75f, 0.0f);

       glScalef(0.25f, 0.25f, 0.25f);
immediately before the code to draw the triangle.  After the code to draw the triangle, add the line
  glPopMatrix();
The green triangle should be one quarter (because of
glScaled) of its previous size and near the top of the screen (because of glTranslated).

26.  Change the glTranslated line to move the triangle based on the xPosition variable:
       glTranslatef(xPosition, 0.75f, 0.0f);
The three parameters are how far to move in the X, Y, and Z directions.  Since we are working in 2D, we aren't using Z.

27.  Compile and run the program.  The green triangle should now move back and forth across the top of the screen.

Adding User Input

28.  Add function keyboard to your program above the idle function:
void keyboard (unsigned char key, int x, int y)
{
  switch(key)
  {
  case
'R':
  case
'r':
       xPosition = 0.0;
       break;
  case 27: // on [ESC]
       exit(0); // normal exit
       break;
  }
}
Add a prototype for this function with the other function prototypes:
void keyboard (unsigned char key, int x, int y);

29.  In the main function, add a call to glutKeyboardFunc to handle keyboard input of ASCII keys immediately before the call to glutIdleFunc:
  glutKeyboardFunc(keyboard);
Compile and run your program.  You should be able to reset the triangle to its starting position with the
[R] key and exit the program with the [ESC] key.

30.  Comment out updating to position in the idle function.
  // xPosition += xIncrement;
  // if(xPosition > 1 || xPosition < -1)
  //   xIncrement = -xIncrement;

31.  Add function special to your program above idle:
void special (int special_key, int x, int y)
{
  switch(special_key)
  {
  case GLUT_KEY_LEFT:
       xPosition -= xIncrement;
       break;
  case GLUT_KEY_RIGHT:
       xPosition += xIncrement;
       break;
  }
}
Add a prototype for this function with the other function prototypes:
void special (int special_key, int x, int y);

32.  In the main function, add a call to glutSpecialFunc to handle the special keys, such as the arrow keys, after the call to glutKeyboardFunc:
  glutSpecialFunc(special);

Compile and run your program.  You should be able to move the green triangle back and forth with the left and right arrow keys.

Exercises:

33.  Change the program so that the 'A'/'a' key causes the triangle to move left and the 'D'/'d' key causes it to move right.  Thus the user can use [A] instead of left arrow and [D] instead of right arrow.  Note: These keys have to be handled by the keyboard function rather than the special function.

34.  Change the program so that the 'W'/'w' key causes the triangle to move up (increase its Y value) and the 'S'/'s' key cause it to move down (decrease its Y value).  The triangle should start at a Y position of 0.75.

·         Note: You will need Y variables similar to your X variables.

·         Note: The glTranslated command takes three parameters, which are movement in the X, Y, and Z directions.  You should be able to move the triangle in both the X and Y directions using one glTranslated command.

35.  Change the program so that the orange pentagon does not spin automatically.  Instead, it should spin clockwise (decrease pentagonDegrees) when the right arrow is pressed and it should spin counterclockwise (increase pentagonDegrees) when the left arrow is pressed.

·         Note: Make a degreesIncrement variable similar to the xIncrement variable.  It will need a larger value, such as 2.0f.

·         Note: The arrow keys should no longer move the triangle.

36.  Change the program so that the orange pentagon is replaced by a purple hexagon.

·         Note: A hexagon has six vertices.

·         Note: You can find the RGB colour components for any colour (such as purple) by doing an Internet search for RGB purple.

·         It is strongly recommended that you draw a hexagon on paper, label the points with (X, Y) values, and then enter the points in order around the hexagon.  If you just edit the points at random, you may put the vertices in an unnatural order, which will cause it to display incorrectly.

37.  Change the program so that the green triangle is replaced by a brown octagon.  You can choose any shade of brown.

·         Note: An octagon has eight vertices.