George Aroush, instructor
- Create your own three dimensional object as a set of vertices and edges in arrays, as in CubeOne.java, CubeTwo.java and CubeThree.java. Display the object, using either the code in those demo programs or something you've developed yourself. As a first step, you might want to display just the vertices; after those look okay, get the edges to show.
- Modify CubeThree.java or your own three dimensional display program to make an object (could be the cube) move in a very different way than the bouncing around in CubeThree.java. Suggestions:
a) a curved or circular path, based on sines and cosines,
b) a random or partially random path,
c) zooms toward the viewer from distant, randomly selected locations,
d) bouncing off of moving walls,
e) something of your own idea.
Back to the top of this page. | Back to main Index |
Notes on 3D
Three dimensional objects are defined in computer graphics as collections of points or vertices, either connected by edges or grouped together in polygonal faces. Displays of edges are called wire-frame displays, are relatively simple to do and are fast to display, rotate, move, etc. Filled-in faces take more time, are a little more difficult to implement, but can look much more realistic. This hand-out will focus on wire-frame displays. Later we well look at filled 3D objects.
3D Coordinates System
3D graphics uses three dimensional coordinates X, Y, and Z-axes. The X and Y axes are the two-dimensional ones we are already used to. The Z axis is perpendicular to them. In the figure below, the X and Y axes are labeled, and the Z axis passes through the paper, where the ( * ) is:
A perspective view of the Z axis is shown in the figure below:
The viewer is considered to be a single eyed, whose eye is located at the origin -- where X, Y, and Z all equal 0. We will be using what is known as a left-handed coordinate system -- in which:
X gets larger to the right,
Y gets larger going up,
Z gets larger going to the front (into the paper).This means that negative Z coordinates are behind the viewer, positive Z coordinates are to the front of the viewer. A point at (50, 60, 70) would be 50 pixels to the right, 60 pixels up and 70 pixels forward, (into the paper.) A point at (20, -15, -90) would be 20 pixels to the right, 15 pixels down and 90 pixels behind the eye (that is behind us.)
Projection
The major problem of wire-frame 3D is getting an object in 3D space to fit, that is to display, on a 2D screen -- how does one project a solid object onto a plane? Think of the viewer as looking through a window located some distance down the Z axis, that is into the paper, right in front of us. This window is the screen, which coincides with the actual video screen a real viewer will look at. The Z-axis passes through the center of the screen. The edges of the screen are paralleled to the X and Y-axes. Think of an object in the 3D space on the other side of the screen from the viewer. That is, the eye is in front of the screen and the screen is in front the object.
"Light-rays" travel form the vertices of the object, through the screen and to the viewer's eye. If we could see the light rays piercing the screen, we could mark those places on the screen with spots. If we connect these spots with lines in the correct order, that is in the order that we connected them when we define the shape, we would have a nice perspective drawing of the object. Every 3D program needs to have a projection function. This function requires the X, Y, and Z-coordinates of each point and the distance of the screen from the viewer. The results are placed in another set of X and Y arrays, which are used for the display. The secret of projection is based on the obvious fact that objects appear smaller when they are farther away and larger when they are closer. It would seem that dividing the X and Y-coordinates of the object by the Z-coordinate in some way would make the resulting projection smaller when the Z is large (the object is far away) and make them larger when the Z is small (the object is nearby.) The resulting function works as follows:
projected_x = x * screen_distance / z projected_y = y * screen_distance / zfor each point. Fortunately, when we project a straight line, the projection is also a straight line. So we only need to project the end points and connect them with a straight line.
The examples programs all work with a cube, because its a common form, and it is very easy to figure out its X, Y, and Z-coordinates. It has eight corners or vertices, but has 12 edges. This is a little more difficult than drawing 2D outline -- we just can't connect the points in order. Instead, we have to tell the program the order to connect them. We can think of the vertices as being numbered form 0 to 7 -- they are stored in arrays from coord[0][X] to coord[7][X], coord[0][Y] to coord[7][Y], etc. Note that X and Y are pre-defined like:
final int X = 0; final int Y = 1;
An array of edges should have two numbers, each representing the index of a vertex. On the cube below, the 12 edges are:
0-1 1-2 2-3 3-0
0-4 1-5 2-6 3-7
4-5 5-6 6-7 7-4Probably (this is true in most cases) the hardest part of doing 3D graphics is figuring out how to connect the edges.
Movement
The three transformations of 3D graphics are translation (movement), scaling, and rotation. We will deal only with movement in this lecture. To move an object, create a three element array trans[], for the amount to move along the X, Y and Z axes. This array and the coordinate arrays are trans[] array are each added to the corresponding X, Y and Z-coordinate for each vertex in coord[][]. When moving 3D objects around, it is easy to go off the screen with the projection. In particular, if the object gets too close to the viewer. Also there is a danger of having Z coordinates equal to zero. Since projection requires dividing the screen distance by the Z-coordinate, this could result in a termination of the program caused by a "division by zero" error. We must always make sure that a vertex is in a safe location before projection. In the demo programs, the values in the coord[] arrays are chosen and kept in limits so that they will always have 2 coordinates greater than 0. So no test is necessary.
Back to the top of this page. | Back to main Index |
CubeOne.java
/* * Program: CubeOne.java * Purpose: 3D Demo * Author: George Aroush * Date: 1/1/1998 * Change Log: None * * Full description of Program: * Projects and displays vertices of stationary cube */ import java.applet.*; import java.awt.*; public class CubeOne extends Applet { final int NP = 8; /* number of points */ final int CX = 320; /* center of screen */ final int CY = 240; final int X = 0; /* the x, y, and z part of the array */ final int Y = 1; final int Z = 2; public void paint(Graphics g) { /* Coordinates of cube. This array holds the shape that we want to define in a 3D space */ float coord[][] = {{75, 75, 325}, {75, -75, 325}, {-75, -75, 325}, {-75, 75, 325}, {75, 75, 475}, {75, -75, 475}, {-75, -75, 475}, {-75, 75, 475}}; int proj[][] = new int[NP][2]; /* This array will hold the output 3D shape */ int i; /* in a 2D form so that we can display it. */ float sd = 300.0f; /* The screen distance */ /* Project cube on screen, & plot its vertices * * Project() needs to know the following: * * coord --> The array holding the defined 3D shape data * proj ---> an array in which the 2D shape will be stored in * NP -----> number of points in the array "coord" * sd -----> how far is the screen form the viewer */ Project(coord, proj, NP, sd); /* calculates where each vertex will be on the 2D screen */ /* Draw the vertex on the screen. We use the fillOval() */ /* method to make the vertex larger so we can see them. */ for (i = 0; i < NP; i++) g.fillOval(proj[i][X], proj[i][Y], 5, 5); } /* * void Project(coord, proj, np, sd) * * Projects coordinate array onto screen at given screen distance storing * result as x and y coordinates in proj[][2] array */ void Project(float coord[][], int proj[][], int np, float sd) { int i, j; 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]); } } }CubeOne.html
<html> <head> <title>
CubeOne</title> </head> <body> <hr> <applet code=
CubeOnewidth=640 height=480></applet> <hr> </body> </html>
CubeTwo.java
/* * Program: CubeTwo.java * Purpose: 3D Movement Demo * Author: George Aroush * Date: 1/1/1998 * Change Log: None * * Full description of Program: * Displays and "bounces" a cube on the screen in a 3D space. * (See week 3's hand-out(s) and demos if you forget how the * bounce program works.) */ import java.applet.*; import java.awt.*; public class CubeTwo extends Applet { final int NP = 8; /* number of points */ final int CX = 320; /* center of screen */ final int CY = 240; final int X = 0; /* the x, y, and z part of the array */ final int Y = 1; final int Z = 2; public void paint(Graphics g) { /* Coordinates of cube. This array holds the shape that we want to define in a 3D space */ float coord[][] = {{75, 75, 325}, {75, -75, 325}, {-75, -75, 325}, {-75, 75, 325}, {75, 75, 475}, {75, -75, 475}, {-75, -75, 475}, {-75, 75, 475}}; float trans[] = {10, 10, 10}; /* velocity -- x, y, and z */ /* Minimum & maximum x, y, & z values for vertex 0 (coord[0][0]) */ float limits[][] = {{-307, 307}, {-261, 261}, {300, 1111}}; /* Holds the x & y points that are the result of projection */ int proj[][] = new int[NP][2]; float sd = 300; int i, j; /* move cube, project it on screen, and display its vertices */ for (j = 0; j < 2000; j++) { /* Translate cube on screen, moving it to a new place * * Translate() needs to know the followings: * * coord --> an array holding the defined 3D shape data * trans --> an array holding the x, y, & z movements * NP -----> number of points in the array "coord" */ Translate(coord, trans, NP); /* Test vertex 0 & reverse direction if it has crossed a limit. Note that vertex 0 */ /* consists of x, y, & z, so our loop will loop three times for one vertex, testing */ /* x, y, & z one by one in the if_() statement in the for_() loop. */ for (i = X; i <= Z; i++) /* "same as:" for (i = 0; i <= 2; i++) */ { if (coord[0][i] < limits[i][0] || coord[0][i] > limits[i][1]) trans[i] *= -1.0; /* reverse direction */ } Project(coord, proj, NP, sd); /*calculates where each vertex will be on the 2D screen */ g.clearRect(0, 0, 640, 480); for (i = 0; i < NP; i++) /* Project cube on screen, & plot its vertices */ g.fillOval(proj[i][X], proj[i][Y], 5, 5); Delay(60); } } /* * void Project(coord, proj, np, sd) * * Projects coordinate array onto screen at given screen distance storing * result as x and y coordinates in proj[][2] array */ void Project(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 Translate(coord, trans, np) * * Add three values of trans[3] to each vertex in coord[][3] thus moving vertices along all three axes */ void Translate(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 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 */ } } }CubeTwo.html
<html> <head> <title>
CubeTwo</title> </head> <body> <hr> <applet code=
CubeTwowidth=640 height=480></applet> <hr> </body> </html>
CubeThree.java
/* * Program: CubeThree.java * Purpose: 3D Movement Demo * Author: George Aroush * Date: 1/1/1998 * Change Log: None * * Full description of Program: * Displays and "bounces" a cube on the screen in a 3D space, * how to display a wire-frame 3D shape, connecting its vertex * by a line segments */ import java.applet.*; import java.awt.*; public class CubeThree extends Applet { final int NP = 8; /* number of points */ final int NE = 12; /* number of edges */ final int CX = 320; /* center of screen */ final int CY = 240; final int MAXX = 512; /* used to check if a line is on the screen */ final int MAXY = 342; final int X = 0; /* the x, y, and z part of the array */ final int Y = 1; final int Z = 2; public void paint(Graphics g) { /* Coordinates of cube. This array holds the shape that we want to define in a 3D space */ float coord[][] = {{75, 75, 325}, {75, -75, 325}, {-75, -75, 325}, {-75, 75, 325}, {75, 75, 475}, {75, -75, 475}, {-75, -75, 475}, {-75, 75, 475}}; /* 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}}; /* velocity -- x, y, and z */ float trans[] = {10, 10, 10}; /* Minimum & maximum x, y, & z values for vertex 0 (coord[0][0]) */ float limits[][] = {{-400, 400}, {-400, 400}, {300, 1111}}; /* Holds the x & y points that are the result of projection */ int proj[][] = new int[NP][2]; float sd = 300; int i, j; /* move cube, project it on screen, and display its vertices */ for (j = 0; j < 2000; j++) { /* Translate cube on screen, moving it to a new place * * Translate() needs to know the followings: * * coord --> an array holding the defined 3D shape data * trans --> an array holding the x, y, & z movements * NP -----> number of points in the array "coord" */ Translate(coord, trans, NP); /* Test vertex 0 & reverse direction if it has crossed a limit. Note that vertex 0 */ /* consists of x, y, & z, so our loop will loop three times for one vertex, testing */ /* x, y, & z one by one in the if_() statement in the for_() loop. */ for (i = X; i <= Z; i++) /* "same as:" for (i = 0; i <= 2; i++) */ { if (coord[0][i] < limits[i][0] || coord[0][i] > limits[i][1]) trans[i] *= -1.0; /* reverse direction */ } Project(coord, proj, NP, sd); /*calculates where each vertex will be on the 2D screen */ g.clearRect(0, 0, 640, 480); /* * Displays a wire-frame of object. * * Display() needs to know the followings: * * proj ---> an array in which the 2D shape are stored * edge----> an array dells display() how to connect the shape * sd -----> number of points in the array "coord" */ Display(proj, edge, NE, g); Delay(60); } } /* * void Project(coord, proj, np, sd) * * Projects coordinate array onto screen at given screen distance storing * result as x and y coordinates in proj[][2] array */ void Project(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 Translate(coord, trans, np) * * Add three values of trans[3] to each vertex in coord[][3] thus moving vertices along all three axes */ void Translate(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 Display(proj, edge, ne) * * Displays wire frame of object. Uses values in edge[][2] to find starting and * ending vertex for each edge */ void Display(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]; /* make sure the the line is on the screen */ if (x1 >= 0 && x1 < MAXX && y1 >= 0 && y1 < MAXY && x2 >= 0 && x2 < MAXX && y2 >= 0 && y2 < MAXY) { 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 */ } } }CubeThree.html
<html> <head> <title>
CubeTwo</title> </head> <body> <hr> <applet code=
CubeThreewidth=640 height=480></applet> <hr> </body> </html>
Back to the top of this page. | Back to main Index. |