Introduction to Computer Graphics and Java Programming for Artists


School of the Museum of Fine Arts ::: Continuing Education

George Aroush, instructor


Lecture Nine -- 3D Graphics -- Part II


 

Readings

  1. The handouts named:
  2. Sample Programs in Batch Eight:
  3. Graphics Book

 

Exercises

    Do before our next lecture

(Do at lest one of the following)

  1. Create your own three dimensional object as a set of vertices and edges in arrays. Display the object, rotate it, move it and scale it in a 3D space.
  2. Modify CubeFour.java or your own three dimensional display program and have the program display two or more objects moving and/or rotating in space all independent of each other.

Back to the top of this page. Back to main Index

 

Three Dimensional Graphics: Part II

Notes on 3D

The demo program CubeFour.java has a complete set of 3D transformations -- scaling, rotating on all three axes, translation and projection. It depicts a domino-shaped object moving toward the viewer, rotating and bobbing around. The program keeps the data for the "cube" in an arrays called oCoord[][3]. The "o" stands for "original" -- the data in these arrays are never changed. Instead the data are copied to an array called, x[], y[], and z[], which undergo all the transformations. For each frame of the display, the cube's data are copied to the transformation arrays, re-scaled, re-rotated on z, x and y-axes, re-moved to current position, re-projected and re-displayed. Then the variables which control the transformations are altered to create movement and spinning. Scaling and rotation are both done with the object centered at the origin. Scaling is done by multiplying each x, y, and z coordinate by a corresponding sx, sy, and sz variables or with a 2D array, by an array called s[3] with a scaling value for the x, y, and z component of each vertex. When coordinates get larger, they move away form the origin -- when they get smaller, they approach it. If the object's center is at the origin, it will be at the origin after the scaling.

3D Rotation

There are three kinds of rotation -- each about an axis. X-axis rotation is like that of a wheel on an axle. Y-axis rotation is like that of dancer turning around. Z-axis rotation is like that of an electric fan. If the object being rotated is not centered at the origin, it will appear to be orbiting around the axis, rather than rotating around itself. The order of the rotations is important. If we rotate an object the same amounts of angles, say N, around its x, then y, then z-axis, it will look different from the way it would appear after rotation about y, then z and then x-axis with the same angle N. One rotation on each axis should be enough to orient an object in any way desired.

Rotation is done with a single Rotate() method that is identical to the 2D rotation method discussed earlier. In fact, the 2D Rotate() method does rotations around "z-axis" of 3D forms.

When an object is rotated about an axis, the coordinates in that axis are not changed. So the method needs the arrays of coordinates from the other two axes. Thus, to do z-axis rotation with Rotate(), we pass it the x[] and y[] arrays. To do x-axis rotation, we pass it the y[] and z[] arrays, and to do y-axis rotation, it needs the z[] and x[] (note that the order must be as stated):

  1st index 2st index
X-axis rotation 1 (Y) 2 (Z)
Y-axis rotation 2 (Z) 0 (X)
Z-axis rotation 0 (X) 1 (Y)

Thus, to use the rotation method we need to specify the following parameters:

Rotate3D(coord, c1, c2, np, angle);
coord the three-dimensional array
c1 1st index
c2 2nd index
np number of points
angle the angle of rotation in radius

Translation, scaling and projection are the same here as we discussed them earlier in the last lecture, and there is full detail about them in the earlier hand outs.

This concludes discussion of wire-frame 3D (but it is only the beginning.) The next step in 3D computer graphics is to define faces or polygons out of the vertices of the object. If the location of a light source is known, the object can be drawn with filled in areas shaded according to the amount of light hitting them. This usually takes a longer time to compute and display, so wire-frame displays are used by computer animators in the early stages of their work to design shapes, positions, movements, etc. before producing a final animation. (We will look into this next lecture.)


Back to the top of this page. Back to main Index

 

Sample Programs -- Batch Nine

ThreeDRot.java

/*
 *  Program:    ThreeDRot.java
 *  Purpose:    3D Rotation
 *  Author:     George Aroush
 *  Date:       1/1/1998
 *  Change Log: None
 *
 *  Full description of Program:
 *      Shows how to do rotation in 3D.
 */

import java.applet.*;
import java.awt.*;

public class ThreeDRot extends Applet
{
    final int   TIME_DELAY = 25;
    final int   NP = 4;     /* number of points */
    final int   NE = 4;     /* number of edges */
    final int   CX = 320;   /* center of screen */
    final int   CY = 240;
    final int   X = 0;
    final int   Y = 1;
    final int   Z = 2;

    public void paint(Graphics g)
    {
            /* coordinates of rectangle */
        float   oCoord[][] = {{50, 150, 0}, {-50, 150, 0}, 
                              {-50, -150, 0}, {50, -150, 0}};

            /* 9th vertex added for center of cube */
        float   coord[][] = new float[NP][3];

            /* index numbers of coordinates for endpoints of edges */
        int     edge[][] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}};

            /* holds the projected 3D object */
        int     proj[][] = new int [NP][2];

            /* the object location in the 3D space */
        float   trans[] = {0, 0, 500};
        
        float   sd = 300;       /* screen distance */
        float   angle;


            /* Copy rectangle from unchanging, original arrays, rotate around */
            /* each three axes, project it on screen, & display its edges. */


    	angle = 0;

        do      /* rotation around x-axes */
        {
            Copy3D(oCoord, coord, NP);

            angle += 0.05f;

            Rotate3D(coord, Y, Z, NP, angle);
            Translate3D(coord, trans, NP);
            Project3D(coord, proj, NP, sd);
            g.clearRect(0, 0, 640, 480);
            Display3D(proj, edge, NE, g);
            Delay(TIME_DELAY);
        }
        while (angle <= 6 * Math.PI);


    	angle = 0;

        do      /* rotation around y-axes */
        {
            Copy3D(oCoord, coord, NP);

            angle += 0.05f;

            Rotate3D(coord, Z, X, NP, angle);
            Translate3D(coord, trans, NP);
            Project3D(coord, proj, NP, sd);
            g.clearRect(0, 0, 640, 480);
            Display3D(proj, edge, NE, g);
            Delay(TIME_DELAY);
        }
        while (angle <= 6 * Math.PI);


        angle = 0;

        do      /* rotation around z-axes */
        {
            Copy3D(oCoord, coord, NP);

            angle += 0.05f;

            Rotate3D(coord, X, Y, NP, angle);
            Translate3D(coord, trans, NP);
            Project3D(coord, proj, NP, sd);
            g.clearRect(0, 0, 640, 480);
            Display3D(proj, edge, NE, g);
            Delay(TIME_DELAY);
        }
        while (angle <= 6 * Math.PI);

        angle = 0;

        do      /* rotation around all three axes */
        {
            Copy3D(oCoord, coord, NP);

            angle += 0.05f;

            Rotate3D(coord, Y, Z, NP, angle);
            Rotate3D(coord, Z, X, NP, angle);
            Rotate3D(coord, X, Y, NP, angle);

            Translate3D(coord, trans, NP);
            Project3D(coord, proj, NP, sd);
            g.clearRect(0, 0, 640, 480);
            Display3D(proj, edge, NE, g);
            Delay(TIME_DELAY);
        }
        while (angle <= 6 * Math.PI);
    }


    /*
     *  void    Project3D()
     *
     *  Projects coordinate array onto screen at given screen distance 
     *  storing result as x and y coordinates in proj[][2] array.
     */
    void    Project3D(float coord[][], int proj[][], int np, float sd)
    {
        int     i;

        for (i = 0; i < np; i++)
        {
            proj[i][X] = CX + (int) (coord[i][X] * sd / coord[i][Z]);
            proj[i][Y] = CY + (int) (coord[i][Y] * sd / coord[i][Z]);
        }
    }


    /*
     *  void    Translate3D()
     *	
     *  Add three values of trans[3] to each vertex in coord[][3] thus 
     *  moving vertices along all three axes
     */
    void    Translate3D(float coord[][], float trans[], int np)
    {
        int	i, j;

        for (i = 0; i < np; i++)    /* loop in the array */
        {
            for (j = 0; j < 3; j++)     /* loop in the vertex */
                coord[i][j] += trans[j];
        }
    }


    /*
     *	void    Copy3D()
     *
     *	Copy X, Y & Z coordinates from original arrays to temp arrays
     */
    void	Copy3D(float orig[][], float coord[][], int np)
    {
        int     i, j;

        for (i = 0; i < np; i++)
        {
            for (j = 0; j < 3; j++)
                coord[i][j] = orig[i][j];
        }
    }


    /*
     *  void    Rotate3D()
     *
     *  Rotate vertices around an axis by number of radians in angle:
     *      for X axis rotation:    c1 = Y and c2 = Z
     *      for Y axis rotation:    c1 = Z and c2 = X
     *      for Z axis rotation:    c1 = X and c2 = Y
     */		
    void	Rotate3D(float coord[][], int c1, int c2, int np, float angle)
    {
        int     i;
        float   t, s, c;

        s = (float) Math.sin(angle);
        c = (float) Math.cos(angle);

        for (i = 0; i < np; i++)
        {
                /* temporarily store new value of coordinate 1 */
            t            = c * coord[i][c1] + s * coord[i][c2];
            coord[i][c2] = c * coord[i][c2] - s * coord[i][c1];
            coord[i][c1] = t;
        }
    }


    /*
     *	void	Display3D()
     *	
     *	Displays wire frame of object.  Uses values in edge[][2] to find starting and 
     *	ending vertex for each edge
     */
    void	Display3D(int proj[][], int edge[][], int ne, Graphics g)
    {
        int     e;
        int     s, f;
        int     x1, y1, x2, y2;

        for (e = 0; e < ne; e++)
        {
            s = edge[e][0];     /* s -- is the starting vertex */
            f = edge[e][1];     /* f -- is the ending vertex */

            x1 = proj[s][X];    /* stating point of a line */
            y1 = proj[s][Y];

            x2 = proj[f][X];    /* ending point of a line */
            y2 = proj[f][Y];

            g.drawLine(x1, y1, x2, y2);
        }  
    }


    /*
     *  void    Delay()
     *
     *  Simply pauses for some time
     */
    public void Delay(int delayTime)
    {
        try
        {
            Thread.sleep(delayTime);    /* call Java's sleep method */
        }
        catch (InterruptedException e)
        {
            /* when the sleep() call above is over, Java will */
            /* be interuppted and we fall into this block of code */
            /* because our intention is simply slow down things */
            /* wed do nothing in our exception code and just get out */
        }
    }
}

ThreeDRot.htm

<html>
<head>
<title>ThreeDRot</title>
</head>
<body>
    <hr>
    <applet code=ThreeDRot width=640 height=480></applet>
    <hr>
</body>
</html>

CubeFour.java

 

/*
 *  Program:    CubeFour.java
 *  Purpose:    3D Rotation
 *  Author:     George Aroush
 *  Date:       1/1/1998
 *  Change Log: None
 *
 *  Full description of Program:
 *      Shows how to do scaling and rotation of a 3D objects.
 */

import java.applet.*;
import java.awt.*;
public class CubeFour extends Applet
{
    final int   TIME_DELAY = 50;
    final int   NP = 9;     /* number of points */
    final int   NE = 12;    /* number of edges */
    final int   CX = 320;   /* center of screen */
    final int   CY = 240;
    final int   X = 0;
    final int   Y = 1;
    final int   Z = 2;

    public void paint(Graphics g)
    {
            /* coordinates of rectangle */
        float   oCoord[][] = {{ 75,  75, -75}, { 75, -75, -75}, {-75, -75, -75}, 
                              {-75,  75, -75}, { 75,  75,  75}, { 75, -75,  75},
                              {-75, -75,  75}, {-75,  75,  75}, { 0,    0,   0}};
            /* 9th vertex added for center of cube */
        float   coord[][] = new float[NP][3];
            /* index numbers of coordinates for endpoints of edges */
        int     edge[][] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}, 
                            {0, 4}, {1, 5}, {2, 6}, {3, 7}, 
                            {4, 5}, {5, 6}, {6, 7}, {7, 4}};
            /* holds the projected 3D object */
        int     proj[][] = new int [NP][2];
            /* the object location in the 3D space */
        float   trans[] = {0, 0, 900};
            /* velocity along X, Y, & Z axes*/
        float   vel[] = {20, 20, 20};
            /* these scaling give the cube a domino-like shape */
        float   scal[] = {0.618f, 1.0f, 1.618f};
            /* current angles of rotation of object */
        float   angle[] = {0, 0, 0};
            /* rates of rotation of object: x, y & z angle */
        float   angInc[] = {0.08f, 0.05f, 0.0722f};
            /* minimum and maximum X Y & Z values for vertex 0 */
        float	limits[][] = {
                                {-400, 400},    /* x limits */
                                {-400, 400},    /* y limits */
                                {500, 5000}     /* z limits */
                             };
        float   sd = 300;       /* screen distance */
        int     i;

            /*
             * Copy cube from unchanging, original arrays, scale and rotate around
             * three axes move it, project it on screen, & display its edges 
             */
        do
        {
            Copy3D(oCoord, coord, NP);
            Scale3D(coord, scal, NP);
            Rotate3D(coord, Y, Z, NP, angle[X]);
            Rotate3D(coord, Z, X, NP, angle[Y]);
            Rotate3D(coord, X, Y, NP, angle[Z]);
            Translate3D(coord, trans, NP);

                /* test center of cube and reverse direction */
                /* of velocity if it has crossed a limit */
            for (i = X; i <= Z; i++)
            {
                if (coord[NP - 1][i] < limits[i][0] || coord[NP - 1][i] > limits[i][1])
                    vel[i] *= -1.0;

                trans[i] += vel[i];     /* change location of object */

                angle[i] += angInc[i];  /* change angles of rotation */

                if (angle[i] > 2.0 * Math.PI)
                    angle[i] -= 2.0 * Math.PI;
            }

            Project3D(coord, proj, NP, sd);

            g.clearRect(0, 0, 640, 480);

            Display3D(proj, edge, NE, g);
            Delay(TIME_DELAY);
        }
        while (true);
    }


    /*
     *  void    Project3D()
     *
     *  Projects coordinate array onto screen at given screen distance 
     *  storing result as x and y coordinates in proj[][2] array.
     */
    void    Project3D(float coord[][], int proj[][], int np, float sd)
    {
        int     i;

        for (i = 0; i < np; i++)
        {
            proj[i][X] = CX + (int) (coord[i][X] * sd / coord[i][Z]);
            proj[i][Y] = CY + (int) (coord[i][Y] * sd / coord[i][Z]);
        }
    }


    /*
     *  void    Translate3D()
     *	
     *  Add three values of trans[3] to each vertex in coord[][3] thus 
     *  moving vertices along all three axes
     */
    void    Translate3D(float coord[][], float trans[], int np)
    {
        int	i, j;

        for (i = 0; i < np; i++)    /* loop in the array */
        {
            for (j = 0; j < 3; j++)     /* loop in the vertex */
                coord[i][j] += trans[j];
        }
    }


    /*
     *	void    Copy3D()
     *
     *	Copy X, Y & Z coordinates from original arrays to temp arrays
     */
    void	Copy3D(float orig[][], float coord[][], int np)
    {
        int     i, j;

        for (i = 0; i < np; i++)
        {
            for (j = 0; j < 3; j++)
                coord[i][j] = orig[i][j];
        }
    }


    /*
     *	void	Scale3D()
     *
     *	Multiplies each X, Y & Z coordinate by corresponding element in the array s[]
     */
    void	Scale3D(float coord[][], float s[], int np)
    {
        int     i, j;

        for (i = 0; i < np; i++)
        {
            for (j = 0; j < 3; j++)
                coord[i][j] *= s[j];
        }
    }


    /*
     *  void    Rotate3D()
     *
     *  Rotate vertices around an axis by number of radians in angle:
     *      for X axis rotation:    c1 = Y and c2 = Z
     *      for Y axis rotation:    c1 = Z and c2 = X
     *      for Z axis rotation:    c1 = X and c2 = Y
     */		
    void	Rotate3D(float coord[][], int c1, int c2, int np, float angle)
    {
        int     i;
        float   t, s, c;

        s = (float) Math.sin(angle);
        c = (float) Math.cos(angle);

        for (i = 0; i < np; i++)
        {
                /* temporarily store new value of coordinate 1 */
            t            = c * coord[i][c1] + s * coord[i][c2];
            coord[i][c2] = c * coord[i][c2] - s * coord[i][c1];
            coord[i][c1] = t;
        }
    }


    /*
     *	void	Display3D()
     *	
     *	Displays wire frame of object.  Uses values in edge[][2] to find starting and 
     *	ending vertex for each edge
     */
    void	Display3D(int proj[][], int edge[][], int ne, Graphics g)
    {
        int     e;
        int     s, f;
        int     x1, y1, x2, y2;

        for (e = 0; e < ne; e++)
        {
            s = edge[e][0];     /* s -- is the starting vertex */
            f = edge[e][1];     /* f -- is the ending vertex */

            x1 = proj[s][X];    /* stating point of a line */
            y1 = proj[s][Y];

            x2 = proj[f][X];    /* ending point of a line */
            y2 = proj[f][Y];

            g.drawLine(x1, y1, x2, y2);
        }  
    }


    /*
     *  void    Delay()
     *
     *  Simply pauses for some time
     */
    public void Delay(int delayTime)
    {
        try
        {
            Thread.sleep(delayTime);    /* call Java's sleep method */
        }
        catch (InterruptedException e)
        {
            /* when the sleep() call above is over, Java will */
            /* be interuppted and we fall into this block of code */
            /* because our intention is simply slow down things */
            /* wed do nothing in our exception code and just get out */
        }
    }
}

CubeFour.htm

<html>
<head>
<title>CubeFour</title>
</head>
<body>
    <hr>
    <applet code=CubeFour width=640 height=480></applet>
    <hr>
</body>
</html>

Back to the top of this page. Back to main Index.