Cover page images (elite)

Laboratorio Grafica al Calcolatore 2009/2010: Lecture 4 - Scene Modeling

Roberto Toldo, <nome.cognome@univr.it >




Hit the space bar for next slide

Previous Lesson Summary

Previous sections described how objects in a 3D world can be viewed. Given a viewing volume, these objects were clipped and projected onto a projection plane. The contents of the projection plane are the `snapshot' of the 3D world. This section describes the transition from the projection plane to the 2D display screen.

The default viewport is usually all you need. The lower left corner of the viewport defaults to the lower left corner of the window, which is (0,0) in window coordinates. The upper right corner is determined by the size of the window. Therefore, when you open a window, the initial viewport is

    glViewport(0, 0, width, height);

where width and height match the size of the window.

Suppose you want to do something fancier within a window. Using the camera analogy again, suppose you put a certain lens on your camera (your projection transformation). Then, you take a picture and develop the film to make slides. A slide is a projection plane, a `snapshot' of the 3D world.

Now, you use a slide projector to display the slide on a wall. You can think of the wall as being analogous to your display screen. If you consider the wall to be your display screen, then the area of the wall which your slide covers is its viewport. By rolling the projector closer to, or further away from, the wall you can reduce or enlarge the display of the slide. This reduction and enlargement is analogous to changing the viewport.

More technically stated, the area within a window where an image is displayed is the viewport. The location of the viewport is specified by window coordinates. The left, right, bottom and top clipping borders of the projection plane are mapped to the corresponding boundaries of the viewport.

Ideally, the aspect ratio of the projection plane is the same as the aspect ratio for the viewport. In this ideal case, the mapping between projection plane and viewport is a simple reduction or enlargement. However, if the aspect ratios of projection plane and viewport are different, there will be distortions. For example, if the viewport is taller and/or thinner than the projection plane, the height of the displayed object will exaggerated. If the viewport is shorter and/or wider than the projection plane, the displayed object will extend its width.

Lecture Objectives

At the completion of this lecture, you will be able to

Modeling Transformation Order

The order in which modeling transformations are executed is important.

For example

    glTranslatef( 1.0, 0.0, 0.0 );
    glRotatef( 60.0, 0.0, 0.0, 1.0 );

is not the same as

    glRotatef( 60.0, 0.0, 0.0, 1.0 );
    glTranslatef( 1.0, 0.0, 0.0 );

Example: order.c

/* order.c - illustrate what happens when the order
 *      of the modeling transformations is
 *
 *            glTranslate( ... );
 *            glRotate( ... );
 *     versus 
 *            glRotate( ... );
 *            glTranslate( ... );
 *
 *     <o> key     - toggle order of transformations
 *     Escape key  - exit program
 */
#include <GL/glut.h>    /* includes gl.h, glu.h */
#include <stdio.h>

/*  Function Prototypes  */

GLvoid  initgfx( GLvoid );
GLvoid  keyboard( GLubyte, GLint, GLint );
GLvoid  drawScene( GLvoid );

void printHelp( char * );

/* Global Definitions */

#define KEY_ESC    27      /* ascii value for the escape key */

/* Global Variables */

static GLboolean rotateFirst = GL_TRUE;

void
main( int argc, char *argv[] )
{
   GLsizei width, height;

   glutInit( &argc, argv );

   width = glutGet( GLUT_SCREEN_WIDTH ); 
   height = glutGet( GLUT_SCREEN_HEIGHT );
   glutInitWindowPosition( width / 4, height / 4 );
   glutInitWindowSize( (width / 2) - 4, height / 2 );
   glutInitDisplayMode( GLUT_RGBA );
   glutCreateWindow( argv[0] );

   initgfx();

   glutKeyboardFunc( keyboard );
   glutDisplayFunc( drawScene ); 

   printHelp( argv[0] );

   glMatrixMode( GL_PROJECTION );
   glOrtho( -2.0, 2.0, -2.0, 2.0, -1.0, 1.0 );
   glMatrixMode( GL_MODELVIEW );

   glutMainLoop();
}

void
printHelp( char *progname )
{
   fprintf(stdout, 
           "\n%s - demonstrates the effect of transformation order\n\n"
           "<o> key            - toggle order of transformations\n"
           "Escape Key         - exit the program\n\n",
           progname);

   fprintf(stdout, "order is Rotate-Translate\n");
}

GLvoid
initgfx( GLvoid )
{
   glClearColor( 0.0, 0.0, 1.0, 1.0 );
   glShadeModel( GL_FLAT );
}

GLvoid 
keyboard( GLubyte key, GLint x, GLint y )
{
   switch (key) {
   case 'o':   /* toggle transformation order */
       rotateFirst = !rotateFirst;
       fprintf(stdout, "order is %s\n",
           (rotateFirst?"Rotate-Translate":"Translate-Rotate"));
       glutPostRedisplay();
       break;
   case KEY_ESC:   /* Exit whenever the Escape key is pressed */
       exit(0);
   }
}

GLvoid
drawScene( GLvoid )
{
   glClear( GL_COLOR_BUFFER_BIT );

   glPushMatrix();
       if ( rotateFirst ) {
           /* Do a rotation first, then a translation */
           glRotatef( 60.0, 0.0, 0.0, 1.0 );
           glTranslatef( 1.0, 0.0, 0.0 );
       } else {
           /* Do a translation first, then a rotation */
           glTranslatef( 1.0, 0.0, 0.0 );
           glRotatef( 60.0, 0.0, 0.0, 1.0 );
       }

       Checkerboard();

       /* Draw X-Y axis -- X is red, Y is green */
       XYaxes();

   glPopMatrix();

   glFlush();
}

Notes:

This program renders a checkerboard under the effect of a translation and a rotation. Press the <o> key to toggle the order of the transformations.

Why Is Order Important?

Notes:

In many cases, the order will be

Effects of Transformation Order

Compositing Modeling Transformations

Moving Coordinate System

glPushMatrix();
glTranslatef( 4.0, 2.0, 0.0 );
glScalef( 1.0, 0.5, 1.0 );

Moving Coordinate System (continued)

glRotatef( 45.0, 0.0, 0.0, 1.0 );
draw_unit_square_box();
glPopMatrix();

Composition Example 1: Robot Arm

Draw two boxes to represent the upper and lower segments of a robot arm.

Step 1: Move to the center of the upper arm

Step 2: Draw box

Step 3: Move to the elbow

Step 4: Rotate 45 degrees about the elbow

Step 5: Move to the center of the lower arm

Step 6: Draw box

Notes:

Each box is drawn centered around the current origin.

Example: robot_arm.c

/* robot_arm.c - draw a robot arm by composing modeling 
 * transformations
 *
 *     Escape Key      - exit program
 */

#include <GL/glut.h>    /* includes gl.h, glu.h */

#include <stdio.h>

/*  Function Prototypes  */

GLvoid  initgfx( GLvoid );
GLvoid  keyboard( GLubyte, GLint, GLint );
GLvoid  drawScene( GLvoid );

void printHelp( char * );

/* Global Definitions */

#define KEY_ESC    27      /* ascii value for the escape key */

void
main( int argc, char *argv[] )
{
   GLsizei width, height;

   glutInit( &argc, argv );

   width = glutGet( GLUT_SCREEN_WIDTH ); 
   height = glutGet( GLUT_SCREEN_HEIGHT );
   glutInitWindowPosition( 0, height / 4 );
   glutInitWindowSize( (width / 2) - 4, height / 2 );
   glutInitDisplayMode( GLUT_RGBA );
   glutCreateWindow( argv[0] );

   initgfx();

   glutKeyboardFunc( keyboard );
   glutDisplayFunc( drawScene ); 

   printHelp( argv[0] );

   glMatrixMode( GL_PROJECTION );
   gluPerspective( 45.0, (GLdouble) width/(GLdouble)height,1.0,20.0 );
   glMatrixMode( GL_MODELVIEW );
   
   glutMainLoop();
}

void
printHelp( char *progname )
{
   fprintf(stdout, "\n%s - renders a robot arm\n\n"
       "Escape Key   - exit the program\n\n",
           progname);
}

GLvoid
initgfx( GLvoid )
{
   glClearColor( 0.0, 0.0, 0.0, 1.0 );
   glShadeModel( GL_FLAT );
}

GLvoid 
keyboard( GLubyte key, GLint x, GLint y )
{
   switch (key) {
   case KEY_ESC:   /* Exit whenever the Escape key is pressed */
       exit(0);
   }
}

GLvoid
drawScene( GLvoid )
{
    static GLfloat    upperArmColor[] = { 0.5, 0.5, 0.8 };
    static GLfloat    lowerArmColor[] = { 0.5, 0.8, 0.5 };
   
    glClear( GL_COLOR_BUFFER_BIT );

    glPushMatrix();
       /* translate into the viewing volume */
       glTranslatef( 0.0, 0.0, -8.0 );

       XYaxes();

       glPushAttrib( GL_LINE_BIT );
       glLineWidth( 2.0 );

       /* Draw the upper arm with the shoulder
        * on the Z axis */

       /* Step 1: move to the center of the upper arm */
       glTranslatef( 1.0, 0.0, 0.0 ); 

       /* Step 2: draw the upper arm */
       glColor3fv( upperArmColor );
       WireBox( 2.0, 0.4, 1.0 );

       /* Draw the lower arm at the end of the upper arm and 
        * rotate it 45 degrees */

       /* Step 3: move to the elbow */
       glTranslatef( 1.0, 0.0, 0.0 );

       /* Step 4: rotate the lower arm 45 degrees */
       glRotatef( 45.0, 0.0, 0.0, 1.0 );

       /* Step 5: move to the center of the lower arm */
       glTranslatef( 1.0, 0.0, 0.0 );

       /* Step 6: draw the lower arm */
       glColor3fv( lowerArmColor );
       WireBox( 2.0, 0.4, 1.0 );

       glPopAttrib();

    glPopMatrix();

   glFlush();
}

Notes:

This program creates a robot arm using two boxes.

Because the box is drawn centered at the current origin, we need to translate half of a box width to get the edge of the box on the z axis.

After drawing the upper arm, we translate to the elbow and rotate 45 degrees before drawing the lower arm.

Once again, we have to translate half of a box width in order to draw the lower arm so that its edge meets at the elbow.

Independent Models

Independent models undergo independent transformations.

Notes:

Push/pop is used to isolate the rest of the program from the effects of a set of transformations.

Example of Models That Are Not Independent

Transformations accumulate.

Notes:

First, the coordinate system is rotated and a tetrahedron is drawn. Then, because transformations accumulate, the beam is translated in the newly rotated coordinate system.

Example of Models That Are Independent

Each model is affected by separate transformations.

Notes:

First, the coordinate system is rotated and a tetrahedron is drawn. Then the previous matrix is restored, and the beam is translated relative to the original coordinate system.

Composition Example 2: Robot Claw

Step 1: Move to the center of the upper arm

Step 2: Draw box

Step 3: Move to the elbow

Step 4: Rotate 45 degrees about the elbow

Step 5: Move to the center of the lower arm

Step 6: Draw box

Step 7: How do you position the third arm?

Robot Claw Example

glMatrixMode( GL_PROJECTION )


gluPerspective(45.0,1.0,1.0,20.0)


glMatrixMode( GL_MODELVIEW )


glPushMatrix()


glTranslatef( 0.0, 0.0, -8.0 )


glTranslatef( 1.0, 0.0, 0.0 )
/* Draw the upper arm */
WireBox( 2.0, 0.4, 1.0 );

Robot Claw Example (continued)

glTranslatef( 1.0, 0.0, 0.0 )


glPushMatrix();


glRotatef( 45.0, 0.0, 0.0, 1.0 )


glTranslatef( 1.0, 0.0, 0.0 );
/* Draw the upper claw */
WireBox( 2.0, 0.4, 1.0 );


glPopMatrix()

Robot Claw Example (continued)

glPushMatrix();


glRotatef( -45.0, 0.0, 0.0, 1.0 )


glTranslatef( 1.0, 0.0, 0.0 );
/* Draw the lower claw */
WireBox( 2.0, 0.4, 1.0 );


glPopMatrix()


glPopMatrix()

Example: robot_claw.c

/* robot_claw.c - draw a robot claw by using the modelview stack
 * to save transformations so that we can get back to the elbow.
 *
 *     Escape Key      - exit program
 */

#include <GL/glut.h>    /* includes gl.h, glu.h */

#include <stdio.h>

/*  Function Prototypes  */

GLvoid  initgfx( GLvoid );
GLvoid  keyboard( GLubyte, GLint, GLint );
GLvoid  drawScene( GLvoid );

void printHelp( char * );

/* Global Definitions */

#define KEY_ESC    27      /* ascii value for the escape key */

void
main( int argc, char *argv[] )
{
   GLsizei width, height;

   glutInit( &argc, argv );

   width = glutGet( GLUT_SCREEN_WIDTH ); 
   height = glutGet( GLUT_SCREEN_HEIGHT );
   glutInitWindowPosition( (width / 2) + 4, height / 4 );
   glutInitWindowSize( (width / 2) - 4, height / 2 );
   glutInitDisplayMode( GLUT_RGBA );
   glutCreateWindow( argv[0] );

   initgfx();

   glutKeyboardFunc( keyboard );
   glutDisplayFunc( drawScene ); 

   printHelp( argv[0] );

   glMatrixMode( GL_PROJECTION );
   gluPerspective( 45.0, (GLdouble) width/ (GLdouble)height, 1.0, 20.0 );
   glMatrixMode( GL_MODELVIEW );

   glutMainLoop();
}

void
printHelp( char *progname )
{
   fprintf(stdout, "\n%s - renders a robot claw\n\n"
           "Escape Key   - exit the program\n\n",
           progname);
}

GLvoid
initgfx( GLvoid )
{
   glClearColor( 0.0, 0.0, 0.0, 1.0 );
   glShadeModel( GL_FLAT );
}

GLvoid 
keyboard( GLubyte key, GLint x, GLint y )
{
   switch (key) {
   case KEY_ESC:   /* Exit whenever the Escape key is pressed */
       exit(0);
   }
}

GLvoid
drawScene( GLvoid )
{
    static GLfloat    upperArmColor[] = { 0.5, 0.5, 0.8 };
    static GLfloat    lowerArmColor[] = { 0.5, 0.8, 0.5 };
   
    glClear( GL_COLOR_BUFFER_BIT );

   /* Save the Identity matrix that's on the ModelView
    * matrix stack */
    glPushMatrix();

       /* translate into the viewing volume */
       glTranslatef( 0.0, 0.0, -8.0 );

       XYaxes();

       glPushAttrib( GL_LINE_BIT );
       glLineWidth( 2.0 );

       /* Draw the upper arm with the shoulder
        * on the Z axis */

       /* Step 1: move to the center of the upper arm */
       glTranslatef( 1.0, 0.0, 0.0 ); 

       /* Step 2: draw the upper arm */
       glColor3fv( upperArmColor );
       WireBox( 2.0, 0.4, 1.0 );

       /* Draw the lower arm at the end of the upper arm and 
        * rotate it 45 degrees */

       /* Step 3: move to the elbow */
       glTranslatef( 1.0, 0.0, 0.0 );

       /* Save the current matrix so we can get back to the elbow */
       glPushMatrix();
           /* Step 4: rotate the lower arm 45 degrees */
           glRotatef( 45.0, 0.0, 0.0, 1.0 );

           /* Step 5: move to the center of the lower arm */
           glTranslatef( 1.0, 0.0, 0.0 );

           /* Step 6: draw the upper claw */
           glColor3fv( lowerArmColor );
           WireBox( 2.0, 0.4, 1.0 );

       /* Discard the last transformations, and retrieve 
        * the previous current matrix */
       glPopMatrix();

       /* Save the current matrix so we can get back to the elbow */
       glPushMatrix();
           /* Step 4: rotate the lower arm -45 degrees */
           glRotatef( -45.0, 0.0, 0.0, 1.0 );

           /* Step 5: move to the center of the lower arm */
           glTranslatef( 1.0, 0.0, 0.0 );

           /* Step 6: draw the lower claw */
           glColor3fv( lowerArmColor );
           WireBox( 2.0, 0.4, 1.0 );
       glPopMatrix();

       glPopAttrib();

   glPopMatrix();

   glFlush();
}

Notes:

This program is the same as robot_arm.c, except that we draw a third box to create a claw.

We surround all of the code in drawScene() with glPushMatrix() and glPopMatrix(). This is so that the transformations do not accumulate on the ModelView stack every time drawScene() is called.We also call glPushMatrix() and glPopMatrix() to draw the two pieces of the claw. This is much easier than trying to backtrack to the elbow.

3D Models Provided by the GLUT Library

Function Description
glutSolidSphere
glutWireSphere
Renders a sphere centered at the modeling coordinate origin
glutSolidCube
glutWireCube
Renders a cube centered at the modeling coordinate origin
glutSolidCone
glutWireCone
Renders a cone oriented along the z axis; the base is at z=0 and the top is at z=height
glutSolidTorus
glutWireTorus
Renders a torus centered at the modeling coordinate origin
glutSolidDodecahedron
glutWireDodecahedron
Renders a 12-sided regular object centered at the modeling coordinate origin; radius is 1.0
glutSolidOctahedron
glutWireOctahedron
Renders an 8-sided regular object centered at the modeling coordinate origin; radius is 1.0
glutSolidIcosahedron
glutWireIcosahedron
Renders a 20-sided regular object centered at the modeling coordinate origin; radius is sqrt(3)
glutSolidTetrahedron
glutWireTetrahedron
Renders a 4-sided regular object centered at the modeling coordinate origin; radius is sqrt(3)
glutSolidTeapot
glutWireTeapot
Renders a teapot centered at the modeling coordinate origin

Notes:

See the GLUT Quick Reference at the end of this module for a description of the parameters for these functions.

Lab: Solar System Matrix Stack Exercise

  1. Create a simple model of our solar system.

    Use glutSolidSphere() to draw the Sun, Earth and Moon. Use 0.7 for the radius of the sun, 0.4 for the earth, and 0.2 for the moon.

    Use modeling transformations to position the spheres as shown above.

  2. Using the Worksheet for Solar System Matrix Stack Exercise (on the following page), list the commands to create the solar system and describe what the matrix stacks will look like.

Notes:

glutSolidSphere draws the sphere so that the north pole is on the z axis. (To see this, you can use glutWireSphere instead). Rotate all of your spheres 90 degrees along the x axis to get them oriented correctly.

Use the distances shown above to determine how far to translate for each orbit. For now, do not worry about where to position the earth and moon in their respective orbits.

Include the following commands in the exercise

Do not worry about function parameters initially. The order of the commands is more important. You can go back and fill in the parameters.

Worksheet for Matrix Stack Exercise

Lab: Scene Modeling

  1. Change directory to ../tutorial/exercises

  2. Create a program called solar.c that contains the code from the solar system exercise.

    Make the sun yellow, the earth blue and the moon gray.

    Add code to toggle between wireframe and solid spheres when the <SPACE> key is pressed.

  3. * Copy your perspective.c program to scene.c, and add some 3D shapes. Use the shapes available from the GLUT library.

    * Apply modeling transformations to your shapes. Be sure to save and restore the matrix stack when you want independent transformations.

    * Model a simple 3D object, such as a cube or tetrahedron.

Notes:

* Indicates an optional laboratory exercise.



Lab: Bike Matrix Stack Exercise (Optional)

  1. Model an old-style bicycle.


    Models for the frame and a wheel are rendered using the functions drawFrame() and drawWheel().

  2. Using the Worksheet for Bike Matrix Stack Exercise on page 5-28, list the commands to create the bike and describe what the matrix stacks will look like. Include the following

    1. Projection and modeling transformations

    2. Matrix mode operations

    3. Moving the model into the viewing frustum

    4. Drawing routines

Order of the commands is more important than function parameters.

GLUT Quick Reference

GLUT 3D Object Rendering Functions

Wireframe and Solid Shapes Notes
void glutWireSphere( 
            GLdouble radius, 
            GLint slices, 
            GLint stacks )

void glutSolidSphere(
            GLdouble radius, 
            GLint slices,
            GLint stacks )

radius = radius of sphere

slices = number of subdivisions around the z axis (like longitude)

stacks = number of subdivisions along the z axis (like latitude)
void glutWireCube( GLdouble size ) 

void glutSolidCube( GLdouble size ) 
size = length of each side of the cube
void glutWireCone( 
            GLdouble base, 
            GLdouble height, 
            GLint slices, 
            GLint stacks )

void glutSolidCone(
            GLdouble base, 
            GLdouble height, 
            GLint slices, 
            GLint stacks )

base = radius of base of cone

height = height of cone

slices = number of subdivisions around the z axis (like longitude)

stacks = number of subdivisions along the z axis (like latitude)
void glutWireTorus( 
            GLdouble innerradius, 
            GLdouble outerradius, 
            GLint nsides, 
            GLint rings ) 

void glutSolidTorus( 
            GLdouble innerradius, 
            GLdouble outerradius, 
            GLint nsides, 
            GLint rings ) 

innerradius = inner radius of torus

outerradius = outer radius of torus

nsides = number of sides for each radial section

rings = number of radial divisions for the torus
void glutWireIcosahedron( void ) 

void glutSolidIcosahedron( void ) 
radius is 1.0
void glutWireOctahedron( void ) 

void glutSolidOctahedron( void ) 
radius is 1.0
void glutWireTetrahedron( void ) 

void glutSolidTetrahedron( void ) 
radius is sqrt(3)
void glutWireDodecahedron( void ) 

void glutSolidDodecahedron( void ) 
radius is sqrt(3)
void glutWireTeapot( GLdouble size ) 

void glutSolidTeapot( GLdouble size ) 
size = relative size of teapot

Using gluLookAt

GLvoid gluLookAt( GLdouble eyeX, GLdouble eyeY, GLdouble eyeZ, 
					GLdouble centerX, GLdouble centerY, GLdouble centerZ,   
					GLdouble upX, GLdouble upY, GLdouble upZ)
gluLookAt defines a viewing transformation that:

Notes:

Call gluLookAt, before drawing in the ModelView stack with identity matrix loaded. In this way the Viewing transformation is applied after all your modeling transformation.

mElite 04: Let's pass to 3D

Using your knowledge about gluPerspective, gluLookAt, and 3D model provided by glut you have to

Then you can use the coriolis definition in coriolis.h for the space ship (you have to download the files eliteutils.h, eliteutils.c, and a new Makefile to compile). If you want to use the coriolis model you have to add
#include "eliteutils.h"
#include "coriolis.h"
in the beginning of the program and use this function call
drawEliteObject(coriolis_numberOfFaces, coriolis_vertex, coriolis_face,
coriolis_material)
to draw the satellite.

These utilities also include the function
renderBitmapString(GLUT_BITMAP_HELVETICA_18, "whatYouWant")
where GLUT_BITMAP_HELVETICA_18 is an example of font and "whatYouWant" is an example of string that you can replace with what you pefer.

You can click >>here<< to download an example of what you have to realize. You have to run
chmod +x melite4b
if you want a successful execution of the program.