Most of you have pasted multiple copies of this code into your display() to create multiple viewports in the glut window.
void reshape(int w, int h) { win_w = w; win_h = h; glViewport(0, 0, (GLsizei) win_w, (GLsizei) win_h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(90, (GLfloat) win_w/(GLfloat) win_h, .5, 30.0); }
There is one problem with this code though. This line,
actually specifies the same viewing volume for each viewport, especially the same aspect ratio (the ration of the width and the height of the viewing volume).gluPerspective(90, (GLfloat) win_w/(GLfloat) win_h, .5, 30.0);
Usually we want set the aspect ratio of the viewing volume to the same as that of the actual viewport in the window. (what happens, if they are different?). You achieve this basically by doing something like,
gluPerspective(90, (GLfloat) current_viewport_width/(GLfloat) current_viewport_height, .5, 30.0);
This is for your final game project, which usually requires two viewports, one for the main game interaction, the other for text message like instructions, current scores, etc.
The text viewport should be only a small part of the entire glut window, either to the bottom or to the right. And make sure you set appropriate view volume aspect ratio for each viewports.
We are mainly concerned with 3 types of transformation.
Viewport transformation maps the entire canvas (imaging it as a perpendicular (to z-axis) plane cut by the viewing pyramid) to the entire viewport inside the glut window. This is simply done by a function call,
glViewport(start_x, start_y, viewport_width, viewport_height);
This one project every space point (a vertex or a starting position of character) onto the canvas. The important things here are the near and far clipping planes, the field of view along y direction, and the aspect ratio. You set projection transformation in this way,
glMatrixMode(GL_PROJECTION); // work on this matrix stack glLoadIdentity(); // clear it to be identity. gluPerspective(90, (GLfloat) win_w/(GLfloat) win_h, .5, 30.0); // set the viewing volume, i.e. the transform matrix.
Projection transformation, as well as modelview transformation, is implemented with a math tool called matrix. And all the transformation applied so far are kept on a data structure called matrix stack. Of all the matrix stacks, we are concerned only with two, one for projection transformation, and the other for modelview transformation.
Modelview transformation is used to transform each point in the 3D space before it is projected onto the canvas.
glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // you put all the transformation code here glVertex3f(1, 0, -2); // this point is transformed by all the transformation set before it, and then projected onto the canvas.
There are mainly 3 types of transformation applying on a space point.
glTranslatef(1, -1, -1); // this translation moves the point to (1, 0, -2) + (1, -1, -1) = (2, -1, -3). glVertex3f(1, 0, -2);
glScalef(2, 1, 2); // this moves the point to (1, 0, -2) componentwise multiply (2, 1, 2) = (2, 0, -4). glVertex3f(1, 0, -2);
glRotatef(90, 0,0,1); // this rotate point (1, 0, -2) aroung z-dir 90 degree to a new position at (0, 1, -2). glVertex3f(1, 0, -2);
// Starting from glVertex*() or glRasterPos*(), and tracing BACKWARD all the transform commands issued, // the transformations are applied to the point in that order, and // this process stops right before the first glLoadIdentity().
glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0,0,-2); glBegin(GL_TRIANGLES); { glVertex3f( -1, 0, 0 ); // the vertex is actually (-1, 0, 0) + (0, 0, -2) = (-1, 0, -2) before projected onto the canvas. glVertex3f( 1, 0, 0 ); // (1,0,0) + (0, 0, -2) = (1, 0, -2). glVertex3f( 0, 1, 0 ); // (0,1,0) + (0, 0, -2) = (0, 1, -2). }
glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0,0,-2); glRoate3f(45, 0f, 1f, 0f); glBegin(GL_TRIANGLES); { glVertex3f( -1, 0, 0 ); // the vertex is actually (-1*0.707, 0, 1*0.707) + (0, 0, -2) = (-0.707, 0, -1.293) before projected onto the canvas. glVertex3f( 1, 0, 0 ); // (1*0.707, 0, -1*0.707) + (0, 0, -2) = (0.707, 0, -2.707). glVertex3f( 0, 1, 0 ); // (0,1,0) + (0, 0, -2) = (0, 1, -2). }
The translation basically move the triangle inside the viewing volume so that it shows up on the viewport.
Now, suppose changing the above code into,
glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0,0,-2); glRoate3f(45, 0f, 1f, 0f); glBegin(GL_TRIANGLES); { glVertex3f( -1, 0, 0 ); // the vertex is actually (-1*0.707, 0, 1*0.707) + (0, 0, -2) = (-0.707, 0, -1.293) before projected onto the canvas. glVertex3f( 1, 0, 0 ); // (1*0.707, 0, -1*0.707) + (0, 0, -2) = (0.707, 0, -2.707). glVertex3f( 0, 1, 0 ); // (0,1,0) + (0, 0, -2) = (0, 1, -2). }
So the location of the triangle before projection is (-.7, 0, -1.3), (.7, 0, -2.7) and (0, 1, -2).
Now try to find the location of the triangle if translation is done first, and then followed by rotation, as follow,
glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glRoate3f(45, 0f, 1f, 0f); glTranslatef(0,0,-2); glBegin(GL_TRIANGLES); { glVertex3f( -1, 0, 0 ); glVertex3f( 1, 0, 0 ); glVertex3f( 0, 1, 0 ); }
A scaling is added to the following code. Again try to find the final vertex positions.
glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glRoate3f(45, 0f, 1f, 0f); glTranslatef(0,0,-2); glScalef(.5, 1, 1); glBegin(GL_TRIANGLES); { glVertex3f( -1, 0, 0 ); glVertex3f( 1, 0, 0 ); glVertex3f( 0, 1, 0 ); }
There is an exception to the rule of "Backing Tracing" to find all the applicable transformations -- any transformations put inside a glPushMatrix() & glPopMatrix() block are ignored.
//glPushMatrix() & glPopMatrix() block localize any transformation occured in between them -- they has no effect to //any space points specified outside the block.
Here is an example of model view matrix stack operation.
void display(void) { /* 0 */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* 1 */ glMatrixMode(GL_MODELVIEW); /* 2 */ glLoadIdentity(); /* 3 */ glTranslatef(0,0,-2); /* 4 */ glPushMatrix(); /* 5 */ glTranslatef(1.0, 0, 0); /* 6 */ glScalef(.5, 1, 1); /* 7 */ glutSolidCube (1.0); /* 8 */ glPopMatrix(); /* 9 */ glTranslatef(-1.0, .0, 0); /* 10*/ glScalef(.5, 1, 1); /* This cube is scaled by line 10, translated by line 9, and skipping all the Push&Pop blocks, translated by line 3. This transformation chain stops right there, since next is line 2, glLoadIdentity(). */ /* 11*/ glutSolidCube (1.0); /* 12*/ glutSwapBuffers(); }
// a global variable rot_x = 0; // inside your display() glRotatef(rot_x, 1, 0, 0); // inside the idle callback function rot_x += 1;
Take a look at glut manual for keyboard, mouse, and motion callback functions, and then try to do interaction from keyboard input, mouse click and/or movement.
// (x,y) is the start point in the glut window, (width, height) are the size of the rectangle area to be read. void Canvas :: ReadGLbuffer(int x, int y, int width, int height) { hReso = width, vReso = height; init(); glReadPixels(x, y, width, height, GL_RGB, GL_FLOAT, rgbs); }
1.3.6