The sample program presented in this section will show you how to use basic 3-D graphics.
It will show you how to set up a perspective view, define and object and transform that object in space.
This section assumes some knowledge of graphics. If you don't know what a word means, you can probably look it up in most graphics books.
The Foley and Van Dam book listed on the next page will definiately have the definitions.
Create an OpenGL window with double buffering enabled. Set up
the view class OnSize and OnPaint message handlers just as they
are in the previous program. Add a RenderScene function to the
document class, but do not put any OpenGL commands into it yet.
First we need to change our viewing coordinate system. gluOrtho2D,
the function we have been calling to set up our projection matrix,
actually creates a 3 dimensional view with the near clipping plane
at z=-1 and the far clipping plane at 1. All of the "2-D"
commands we have been calling have actually been 3-D calls where
the z coordinate was zero. Surprise! You've been doing 3-D programming
all along. To view our cube, we would like to use perspective
projection. To set up a perspective projection we need to change
OnSize to the following:
void CGLSample4View::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
GLsizei width, height;
GLdouble aspect;
width = cx;
height = cy;
if (cy==0)
aspect = (GLdouble)width;
else
aspect = (GLdouble)width/(GLdouble)height;
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, aspect, 1, 10.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDrawBuffer(GL_BACK);
}
For those who didn't heed my warning above, orthogonal projection
maps everything in three dimensional space onto a two dimensional
surface at right angles. The result is everything looks the same
size regardless of its distance from the eye point. Perspective
project simulates light passing through a point (as if you were
using a pinhole camera). The result is a more natural picture
where distant object appear smaller. The gluPerspective call above
sets the eye point at the origin, gives us a 45 angle field of
view, a front clipping plane at 1, and a back clipping plane at
10.
Now lets draw our cube. Edit RenderScene to look like this:
void CGLSample4Doc::RenderScene(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glPushMatrix();
glTranslated(0.0, 0.0, -8.0);
glRotated(m_xRotate, 1.0, 0.0, 0.0);
glRotated(m_yRotate, 0.0, 1.0, 0.0);
glBegin(GL_POLYGON);
glNormal3d( 1.0, 0.0, 0.0);
glVertex3d( 1.0, 1.0, 1.0);
glVertex3d( 1.0, -1.0, 1.0);
glVertex3d( 1.0, -1.0, -1.0);
glVertex3d( 1.0, 1.0, -1.0);
glEnd();
glBegin(GL_POLYGON);
glNormal3d( -1.0, 0.0, 0.0);
glVertex3d( -1.0, -1.0, 1.0);
glVertex3d( -1.0, 1.0, 1.0);
glVertex3d( -1.0, 1.0, -1.0);
glVertex3d( -1.0, -1.0, -1.0);
glEnd();
glBegin(GL_POLYGON);
glNormal3d( 0.0, 1.0, 0.0);
glVertex3d( 1.0, 1.0, 1.0);
glVertex3d( -1.0, 1.0, 1.0);
glVertex3d( -1.0, 1.0, -1.0);
glVertex3d( 1.0, 1.0, -1.0);
glEnd();
glBegin(GL_POLYGON);
glNormal3d( 0.0, -1.0, 0.0);
glVertex3d( -1.0, -1.0, 1.0);
glVertex3d( 1.0, -1.0, 1.0);
glVertex3d( 1.0, -1.0, -1.0);
glVertex3d( -1.0, -1.0, -1.0);
glEnd();
glBegin(GL_POLYGON);
glNormal3d( 0.0, 0.0, 1.0);
glVertex3d( 1.0, 1.0, 1.0);
glVertex3d( -1.0, 1.0, 1.0);
glVertex3d( -1.0, -1.0, 1.0);
glVertex3d( 1.0, -1.0, 1.0);
glEnd();
glBegin(GL_POLYGON);
glNormal3d( 0.0, 0.0, -1.0);
glVertex3d( -1.0, 1.0, -1.0);
glVertex3d( 1.0, 1.0, -1.0);
glVertex3d( 1.0, -1.0, -1.0);
glVertex3d( -1.0, -1.0, -1.0);
glEnd();
glPopMatrix();
}
Add member variables to the document class for m_xRotate and m_yRotate
(look at the function definitions to determine the correct type).
Add member variables and event handlers to the view class to modify
the document variables when you drag with the left mouse button
just like we did in the last example (hint: Handle the WM_LBUTTONDOWN,
WM_LBUTTONUP, and WM_MOUSEMOVE events. Look at the sample source
code if you need help). Compile and run the program. You should
see a white cube that you can rotate. You will not be able to
see any discernible feature yet since the cube has no surface
definition and there is no light source. We will add these features
next.
Add the following lines to the beginning of RenderScene:
GLfloat RedSurface[] = { 1.0f, 0.0f, 0.0f, 1.0f};
GLfloat GreenSurface[] = { 0.0f, 1.0f, 0.0f, 1.0f};
GLfloat BlueSurface[] = { 0.0f, 0.0f, 1.0f, 1.0f};
These defines surface property values. Once again, the numbers
represent the red, green, blue and alpha components of the surfaces.
The surface properties are set with the command glMaterial. Add
glMaterialCalls to the following locations:
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, RedSurface);
glBegin(GL_POLYGON);
...
glEnd();
glBegin(GL_POLYGON);
...
glEnd();
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, GreenSurface);
glBegin(GL_POLYGON);
...
glEnd();
glBegin(GL_POLYGON);
...
glEnd();
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, BlueSurface);
glBegin(GL_POLYGON);
...
glEnd();
glBegin(GL_POLYGON);
...
glEnd();
These new calls make two of the cube faces red, two faces green,
and two faces blue. The commands set the ambient color for front
and back of each face. However, the cube will still appear featureless
until the lighting model is enabled. To do this add the following
command to the end of CGLSample4View::OnSize:
glEnable(GL_LIGHTING);
Compile and run the program. You should see one of the blue faces
of the cube. Rotate the cube with your mouse. You will notice
the cube looks very strange. Faces seem to appear and disappear
at random. This is because we are simply drawing the faces of
the cube with no regard as to which is in front. When we draw
a face that is in back, it draws over any faces in front of it
that have been drawn. The solution to this problem is z-buffering.
The z-buffer holds a value for every pixel on the screen. This
value represents how close that pixel is to the eye point. Whenever
OpenGL attempts to draw to a pixel, it checks the z-buffer to
see if the new color is closer to the eye point than the old color.
If it is the pixel is set to the new color. If not, then the pixel
retains the old color. As you can guess, z-buffering can take
up a large amount of memory and CPU time. The cDepthBits parameter
in the PIXELFORMATDESCRIPTOR we used in SetWindowPixelFormat defines
the number of bits in each z-buffer value. Enable z-buffering
by adding the following command at the end of OnSize:
glEnable(GL_DEPTH_TEST);
We also need to clear the z-buffer when we begin a new drawing.
Change the glClear command in RenderScene to the following:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
Compile and run the program to see the results.
We now have a colorful cube that rotates in space and draws correctly,
but it is very faint. Let's add a light to the scene so that we
can see the cube better. Add the following declaration to the
beginning of RenderScene:
GLfloat LightAmbient[] = { 0.1f, 0.1f, 0.1f, 0.1f };
GLfloat LightDiffuse[] = { 0.7f, 0.7f, 0.7f, 0.7f };
GLfloat LightSpecular[] = { 0.0f, 0.0f, 0.0f, 0.1f };
GLfloat LightPosition[] = { 5.0f, 5.0f, 5.0f, 0.0f };
These will serve as the property values for our light. Now add
the following commands just after glClear in RenderScene:
glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, LightSpecular);
glLightfv(GL_LIGHT0, GL_POSITION, LightPosition);
glEnable(GL_LIGHT0);
glLight defines properties for light sources. OpenGL's light sources
are all created within the implementation of OpenGL. Each light
source has an identifier GL_LIGHTi where i is zero
to GL_MAX_LIGHTS. The above commands set the ambient, diffuse,
and specular properties , as well as the position, of light zero.
glEnable(GL_LIGHT0) turns on the light.
The program is currently wasting time by drawing the interior
faces of the cube with our colored surfaces. To fix this, change
the GL_FRONT_AND_BACK parameter in all of the glMaterialfv calls
to GL_FRONT. We also want to set the diffuse reflectivity of the
cube faces now that we have a light source. To do this, change
the GL_AMBIENT parameter in the glMaterialfv calls to GL_AMBIENT_AND_DIFFUSE.
Compile and run the program.
You now have a program that displays a lighted, multi-colored
cube in three dimensions that uses z-buffering and double buffering.
Go ahead and pat yourself on the back. You deserve it.
Download Example Source
Source code for GLSample4
Place the file in the parent directory for the project and type "pkunzip -d GLSamp4.zip".
Then choose File - OpenWorkspace and open the glsample1.mak file unzipped into the GLSample4 directory.
You must have a version on PKZip that supports long file names.
Click here to get the latest version of PKZip.