This week, we'll build 3d objects from polygons, and learn how to get them into position to view them.
What else could we build, but a cube? This one occuples the space between (-0.5, -0.5, -0.5) and (0.5, 0.5, 0.5). Its polygons are defined by six code segments like this:
glBegin(GL_POLYGON);
glVertex3f(-0.5f, 0.5f, 0.5f);
glVertex3f(-0.5f, -0.5f, 0.5f);
glVertex3f(0.5f, -0.5f, 0.5f);
glVertex3f(0.5f, 0.5f, 0.5f);
glend();
Of course the next five segments have different numbers in them. The order of the points is such that the fronts of all polygons face outward. Let's call the whole thing a procedure named CAPdrawCube(). That's our CAP5725 tool for drawing this cube.
What is really happening? The calls to glVertex3f (inside the glBegin and glEnd bracket functions) are not storing anything (that your program can get at, anyhow.) These calls will directly result in the production of an image (at the latest, when you get to glFlush().
JMM's own Model Data Structures. Personally, I don't like to type lots of numbers into flat code like this, so I prefer to go ahead and construct a personal set of data structures and utility functions. After they're built, I can construct my cube like this. First, I create eight vertices.
JMMvertex(1, -0.5, -0.5, -0.5);
JMMvertex(2, -0.5, -0.5, 0.5);
etc...
etc., until I've defined eight vertices named (actually, 'indexed') 1..8. These would be stored in dynamically allocated records, or (in C++) in new objects of a JMMvert class.
Then I would have a single procedure, perhaps called JMMmakePolygon(n,a,b,c,d) which would similarly construct a dynamic object that accessed the vertex records, and was accessible as 'polygon n'. Finally, I would need to have a routine namedJMMdrawPolygon(n). Only inside JMMdrawPolygon, would we actually have code that involved glBegin(GL_POLYGON), and glVertex3f, etc.
Why bother? Well, later we will want to be able to work with the parts of a model (e. g. the horses on a carousel), and move them with respect to one another. It's very handy to have the model data stored in a structure of our own. Then later, we can then replace the calls to JMMvertex above, with our own mini-CAD system that constructs objects, moves their parts around, make (or point to) copies of them (how many horses are there on the carousel?) and displays the results.
You don't need to use my style or conventions; but you will need to build your own model data structures in order to conduct Lab 2 and 3. You might as well do so for Lab 1, if you can.
(JMM=Jack Michael Moshell. That's me, your professor.)
The picture you get when you run the above code, looks like a square - not a cube. The default projection in openGL is a parallel projection. On page 216 we can see that the projection uses a viewport from (-1, -1) to (1,1) in some plane or other, such as Z=0 (for parallel projection, it could be any value of Z of course.)
In order to define the "synthetic camera", we specify
the viewing frustrum or viewing volume. The
frustum (which means "sawed off cone") is a simplified
means of providing all the camera parameters for the full synthetic
camera - View Up, Viewplane Normal, VRP, etc. (see Chapter 6,
FVD.) See the picture below (my first COLOR picture in these notes!
Hope it prints OK in black and white, if you print it.

To specify the frustum, we need six numbers: left, right, top,
bottom, near and far coordinates. The "x and y" parameters
(min and max of each) apply to the near plane, which will serve
both as the near clipping plane and the viewscreen. Let's start
off with
glMatrixMode(GL_PROJECTION);
glLOadIdentity();
glFrustum(-1.0, 1.0, -1.0, 1.0, 2.0, 7.0);
which means that everything we want to see will have z coordinates between 2 and 7. Its X and Y coordinate maxima vary, depending on where in Z the point is. The sides of the frustum slope, after all.
OpenGL has now used these frustum parameters to set up the Synthetic Camera - a mapping which will bring 3d data into 2d screen coordinates when we draw it.
But I can't see anything. Well, if your hat isn't down over your eyes, then you can't see anything because the cube we defined has Z values between -0.5 and 0.5. It's back at the origin. We have to add a model view transformation, which will move the data just before displaying it.
glMatrixMode(GL_MODELVIEW)
This command tells the system which matrix we're talking to.
glLoadIdentity();
glTranslatef(0.0f, 0.0f, -3.5f);
This loads an identity matrix (always have to start with some kind of matrix, because all the rest of these matrix ops are in fact cumulative, on top of whatever you previously had.) Then it multiplies in a translate matrix, so that the current matrix will push its data down the negative Z axis as requred. (We note that OpenGL, unlike the Foley and van Dam text, uses right handed coordinates.)
Then we just call CAPcube, then GlFlush() to force the output.
Here it all is, summarized.
// 1. The projection system------------------------------------
glMatrixMode(GL_PROJECTION);
glLOadIdentity();
glFrustum(-1.0, 1.0, -1.0, 1.0, 2.0, 7.0);
// 2. The model transformations -----------------------------------
glMatrixMode(GL_MODELVIEW)
glLoadIdentity();
glTranslatef(0.0f, 0.0f, -3.5f);
// 3. Now draw your stuff. -----------------------------------
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
CAPdraw();
glFlush();
Why ModelView? Well, transformations to the model, and
transformations of the viewpoint, are indistinguishable (except
for sign)
Scaling. We could have stuck more stuff in the matrix before we drew the cube. For instance if we wanted to scale it, we'd have replaced section 2 above with this:
// 2. The model transformations CORRECTED FROM THE BOOK
------
glMatrixMode(GL_MODELVIEW)
glLoadIdentity();
glScalef(2.0f, 1.0f, 1.0f); // double X, keep Y the same.
glTranslatef(0.0f, 0.0f, -3.5f);
NOTE: The text shows these ops in the reverse order! But you know what would happen if you translated first and then scaled, don't you? In fact, in their working code (pages 242-254), they show scaling, then rotation, then translation.
Rotation about a specific axis specified by (x,y,z) is
constructed (the matrix is constructed) by glRotatef(n,x,y,z)
where n=the number of degrees that the object will rotate about
the axis given by x,y,z. In our example program we're going to
rotate the cube around each axis in turn. We replace section 2
with this:
// 2. The model transformations CORRECTED FROM THE BOOK
------
glMatrixMode(GL_MODELVIEW)
glLoadIdentity();
glRotatef(20.0f,1.0,0.0,0.0);
glRotatef(20.0f,0.0,1.0,0.0);
glRotatef(20.0f,0.0,0.0,1.0);
glTranslatef(0.0f, 0.0f, -3.5f); // This has to come after
rotation!
What control do we have over how much of the "infinite viewing plane" is rendered? We control this with glViewport(left, bottom, width, height). Four integers, almost. Actually left and bottom are GLint, and width and height are GLsizei, a close relative ofGLint. GLint can be negative; GLsizei cannot.
In the demo code, glViewport will be controlled by the Windows message WM_SIZE, so that whenever the user resizes the window, the viewport will automatically be reset to accomodate. In particular, a new window aspect ratio (the ratio of width to height) will change the way things must be drawn.
Here's one way that is done.
void COpengl3dView:: OnSize(UINT nType, int cx, int cy)
{
CView:: OnSize(nType, cx, cy); // Our OnSize calls CView's
ONsize
// to get data.
CCclientDC clientDC(this); // A new DC is created for
this window
wglMakeCurrent(clientDc.m_hDC, m_hRC); // and bound to OpenGL's
RC
glMatrixMode(GL_PROJECTION); // what kind of matrix are
we fixin?
glLoadIdentity();
glFrustum(-1.0, 1.0, -1.0, 1.0, 2.0, 7.0);// Set up the
projection matrix
glViewport (0,0,cx, cy); // set the viewport with nrs
from win.
wglMakeCurrent(NULL, NULL); // makes the RC not current
(unbound.)
}
There's another way to specify the viewing volume that's more intuitive, involving view angle ("field of view, vertical"), aspect ratio (width to height) and the distances to the near and far clipping planes. To wit:
gluPerspective(viewAngle, aspectRatio, nearPlane, farPlane);
All the arguments are GLdouble types.
involves making a kind of interactive demo of the three modelview
transformations so far shown, plus the perspective and viewport
transformations. Read the code, play with it.