Goal: Learn the basics of OpenGL by trying it out!
Action:
NOTE: As you edit code you will be adding, removing and/or changing the code. Do not delete anything: comment out changes so I can see your edits.
This lab works on the current version of Visual C++ Express (VC++) installed on the Olin 102 machines. Other versions may require modification.
Download openGL.cpp from Blackboard to
the project folder. Open the file from the VC++ interface. Right-click on MyOpenGL (under Soluton 'MyOpenGL' in the solution explorer on the left), select "AddExistingItem..." and navigate to select the OpenGL.cpp file.
Build the project and run it. The application opens two windows; one is a console window and the
other is an OpenGL window. The latter shows a
red triangle.
glutInitWindowSize(400, 600);sets the window width to 400 and height to 600. Change this line to
glutInitWindowSize(400, 400);The line
glutCreateWindow("My First OpenGL program");creates the OpenGL window with the specified title. Change the string to "My Second OpenGL program." If you are still running the original application ("My First OpenGL program") exit by closing both windows. Then recompile and run the new version. (If try to recompile while the application is running you'll get a link error.) The OpenGL window should be square and have the new title.
cout << "In display." << endl;at the beginning of the display() function. Recompile and run the program. You should see the message written to the console window. This is because glut automatically calls display() when the program starts. We tell glut the name of our function that draws the OpenGL window by registering it for callback in main() with the command
glutDisplayFunc(display);
glClear(GL_COLOR_BUFFER_BIT);in display() draws the background of the OpenGL window. Comment out this line of code, recompile, and run the program. What happens? (If nothing happens try resizing the window to make it larger.) Uncomment the line before continuing.
glClearColor(0,0,0,0);Replace this line with
glClearColor(0,0,1,0);then recompile the program and run it. The background of the OpenGL window should be blue.
// draw a red triangle glColor3f(1,0,0); glBegin(GL_TRIANGLES); glVertex3f(-3,-1,-8); glVertex3f(3,-1,-10); glVertex3f(0,3,-9); glEnd();in display() draws a red triangle with vertices at (-3,-1,-8),(3,-1,-10), and (0,3,-9). OpenGL uses a right handed coordinate system; from the viewer's perspective positive x is to the right, positive y is up, and positive z is out of the screen toward the viewer.
// draw a green triangle glColor3f(0,1,0); glBegin(GL_TRIANGLES); glVertex3f(-1,-1,-5); glVertex3f(1,1,-5); glVertex3f(0,1,-5); glEnd();Recompile and run the program. You should now see a green triangle in front of the red one.
// enable depth buffering glEnable(GL_DEPTH_TEST);in the init() function. The depth buffer is established by glut. To tell glut we need one, change the line
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);in main() to
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);Finally, change the code
// clear buffers glClear(GL_COLOR_BUFFER_BIT);in display() to
// clear buffers glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);to clear the depth buffer before the window is drawn. Recompile the code and run the program. The green triangle should appear in front of the red one.
glColor3f(1,0,1); glTranslatef(0,0,-5); glutSolidSphere(1,10,10);in display() after the triangle code. Recompile the code and run. You should see a purple sphere-like object.
// draw a red triangle glColor3f(1,0,0); glRotatef(90, 0,0,1); glBegin(GL_TRIANGLES); glVertex3f(-3,-1,-8); glVertex3f(3,-1,-10); glVertex3f(0,3,-9); glEnd();Recompile and run the program. The red triangle should now be rotated. OpenGL supports three types of transformations; rotations, translations, and scaling. We'll continue to explore transformations in the next few step.
float move=0;and an idle() function that updates move.
void idle() { static float increment=-.0001; move+=increment; if (move>2 || move <-20) increment *= -1; glutPostRedisplay(); }Register this function for callback with the following line
glutIdleFunc(idle);in main() where the other callback functions are registered. Also include the function prototype at the top of the file with the other function declarations.
// draw a green triangle glColor3f(0,1,0); glTranslatef(0,0,move); glBegin(GL_TRIANGLES); glVertex3f(-1,-1,-5); glVertex3f(1,1,-5); glVertex3f(0,1,-5); glEnd();This causes the triangle to be translated from its specified position by 0 in the x direction, 0 in the y direction, and move in the z direction. As move is updated the triangle will move backward and forward through our 3D world. Comment out the red-triangle drawing code so that we can watch the green triangle as it moves backwards. Compile and run the program. You should see the triangle moving back and (if you wait a really, really long time) forward but it should be flickering. To fix this, we need to use double buffering . Change the line
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);in main() to
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);and replace the line
glFlush();in display() with
glutSwapBuffers();Recompile the program and run it. The flickering should be gone.
glTranslatef(0,0,move);with
glTranslatef(0,0,move); glTranslatef(0,move,0);Recompile and run the program. The triangle should move down as well. The same effect can be achieved by a single call
glTranslatef(0,move,move);Verify this by modifying the code. We'll see more about composing transformations in the next step.
glLoadIdentity();at the start of display() . This command re-initalized the modelview matrix to the identity each time display is called. Now the translation in the i-th call of display() is -.01*(i-1). You may notice that the movement is much slower. To speed it up, change the size of increment in the idle() function. The animation speed depends on the the system you are using to run the program.To achieve a constant frame rate (that is machine independent) we should build a timer function. But that is beyond the scope of this tutorial.
// draw green triangle glColor3f(0,1,0); glPushMatrix(); glTranslatef(0,move,move); glBegin(GL_TRIANGLES); glVertex3f(-1,-1,-5); glVertex3f(1,1,-5); glVertex3f(0,1,-5); glEnd(); glPopMatrix();The command glPushMatrix() instructs OpenGL to push a copy of the current modelview matrix onto a matrix stack. At this point in our code the modelview matrix is set to the identity. The glPopMatrix() command pops the matrix off the top of the stack and sets it as the current modelview matrix. Now the translate command affects the green triangle but not the red one. It is very important that every glPushMatrix() has a corresponding glPopMatrix() and vice versa!
gluPerspective(45,1,1,100)in init(). The first parameter is the half-height angle. The next parameter is the aspect ratio of the front face. The next two parameters specify the distance from the viewpoint to the near and far planes. The view volume shape and its specification are described in the following figure.
gluPerspective(45.0, 1.0, 1.0, 6.0);You should also disable the animation. Then recompile and run the program. The red triangle should not appear. Why?
gluPerspective(45.0, 1.0, 6.0, 100.0)and recompile the program. Now the green triangle should disappear. Why? Try changing the other parameters and notice the effect they have on the OpenGL window. Restore the original settings before proceeding to the next step.
glViewport(0,0,width,height);This command tells OpenGL to map the lower left corner of the projected image to the coordinate (0,0) of the OpenGL window; (0,0) is the lower left corner of the window. It also specifies that the upper right corner of the projected image is mapped to the coordinate(width,height) in window space; this is the upper right corner of the OpenGL window.
glViewport(0,0,width/2,height/2);then recompile and run the program. Our scene will be drawn in the lower left quarter of the OpenGL window. Change the code back to its original setting and recompile.
void reshape(int width, int height) { if (width<height) glViewport(0,0,width,width); else glViewport(0,0,height,height); }
gluLookAt(0,0,-20,0,0,0,0,1,0);This command moves the viewpoint to the point (0,0,-20). The viewer is looking toward the point (0,0,0). The up direction is <0,1,0>. Recompile and run the program. From the new viewpoint, the red triangle occludes the green one.
void keyboard(unsigned char key, int x, int y) { if (key == ' ') cout << "You hit the space bar." << endl; }Register the keyboard callback with
glutKeyboardFunc(keyboard);and specify the function prototype at the top of the file. Recompile and run the program. Make sure the OpenGL window is active (click on it) then press the space bar. Your program should write "You hit the space bar." to the console window. Note that the keyboard function is only called when the OpenGL window is active. If you click on the console window and then press the space bar your program won't respond.
void keyboard(unsigned char key, int x, int y) { int val; cout << "You entered " << key << "." << endl; cout << "Now enter an integer." << endl; cin >> val; cout << "You entered " << val << "." << endl; }Compile and run the program. Click on the OpenGL window to activate it then press any key. Your program should output the first two messages to the console window. Click on the console window. Type a number and press return. The program should print the final message and the number you input. Try repeating the process without activating the console window. What happens and why? That will be useful to remember!
void mouse(int button, int state, int x, int y) { if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) cout << "You pressed the left mouse button down with the cursor at" << x << " " << y << endl; }Register the mouse function with
glutMouseFunc(mouse);and specify the function prototype.
Deliverables: Email a your OpenGL.cpp to me and hand in a hard-copy in class next Tuesday.