Download openGL.cpp to
the project folder. Open the file from the VC7 interface and right-click to add it to the project.
Build the project and run it. The application opens two windows; one is a console window and the
other is the OpenGL window. The latter shows a
red triangle.
glutInitWindowSize(400, 600);sets the window width to 400 and height to 600 (It also specifies the size of the frame buffer). 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 you 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. glFlush();causes the frame buffer to be transferred to the screen. Add the line
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 screen needs to be drawn. If you resize the window, you'll see that display() is called again.
We tell glut that display() is the function that draws to the frame buffer 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.
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);in main() to
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);OpenGL has a state variable that controls whether or not hidden-surface removal is performed. To set this variable, add the code
// enable depth buffering glEnable(GL_DEPTH_TEST);in the init() function. Finally, we need to re-set the depth buffer each time we re-draw the scene. Change the code
// clear buffers glClear(GL_COLOR_BUFFER_BIT);in display() to
// clear buffers glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);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.
glutSolidSphere(1,10,10)call to
glutSolidSphere(0.75f,20,20)Recompile and run the program. The sphere-like object should be smaller but more sphere-like. The first parameter to glutSolidSphere() is the radius of the sphere; the second two parameters control the resolution of the sphere.
// 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 90 degrees about the z axis. The functions glScalef(float sx, float sy, float sz) and glTranslatef(float dx, float dy, float dz) perform the scale and translate tranforms. If we wanted to scale using doubles, we would use the function glScaled(double sx, double sy, double sz). Yes, OpenGL was developed long ago, before modern concepts like polymorphism were even considered.
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();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 awhile) forward. You probably will notice that the display is 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. Now we have two frame buffers, only one of which is being displayed at any time. We draw to the second buffer and when the drawing is complete the buffers are swapped.
glMatrixMode(GL_MODELVIEW); glLoadIdentity();When the display() function is called the first time in our application, the modelview matrix is set to the identity, move= 0, and no translation occurs. After the scene is drawn the program goes into idle mode, the idle() function is called, move is updated, and the glutPostRedisplay() function is called. When idle() completes, display() is called a second time. The modelview matrix is still set to the identity matrix but move=-.01. The modelview matrix is updated to reflect a translation in y by -.01. The process repeats. The third time display() is called, the modelview matrix holds the translation by -.01 and move is -.02. The modelview matrix is updated to reflect an additional translation of -.02 for a total of -.03. The process continues.
glMatrixMode(GL_MODELVIEW); glLoadIdentity();at the start of display() . Now the translation in the i-th call of display() is -.01*(i-1). You should 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 system you are using to run the program.To achieve a constant frame rate (that is machine independent) we could build a timer function.
// 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. Pushing and popping the modelview matrix makes it very easy for us to render scene graphs. One this to remember, 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,1,1,6);You should also disable the animation. Then recompile and run the program. The red triangle should not appear. Why?
gluPerspective(45,1,6,100)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.
glutReshapeFunc(reshape);in main(). Currently, the reshape() function simply changes the mapping between the image projected on the view window and frame buffer. The command
glViewport(0,0,width,height);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
void reshape(int width, int height)
{
if (width<height)
glViewport(0,0,width,width);
else
glViewport(0,0,height,height);
}
Recompile and run the program. Now the view window is always mapped to a square in the OpenGL window so
its aspect ratio is preserved.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.