| Charles Brian Quinn - CS4451 B |
Individual Project 3: Cylinders, Trees, and Shadows
| Shade a unit cone of radius 1 and height 1 |
- Become familiar with gluCylinder()
- Use it to shade a cone with radius 1 and height 1
In order to display a cone, we must use gluCylinder(). The gluCylinder() C specification is as follows:
void gluCylinder(GLUquadricObj *qobj,
GLdouble baseRadius,
GLdouble topRadius,
GLdouble height,
GLint slices,
GLint stacks)
We create a static quadric object pointer:
GLUquadricObj *quadratic;
And in the init() procedure initialize the quadric object for use in our gluCylinder() calls:
quadratic = gluNewQuadric(); gluQuadricNormals(quadratic, GLU_SMOOTH); gluQuadricTexture(quadratic, GL_TRUE);
To display the cone, we simply call gluCylinder with a base radius of 1, a top radius of 0, and a height of 1 to make the cone:
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_blue); gluCylinder(quadratic,1.0f,0.0f,1.0f,32,32);
| Shade two different chairs |
- Use glTranslatef(), glScalef(), and glRotatef() to transform coordinate
systems and to make two chairs in different styles by drawing instances of the
cone.
- Show an image of a model containing both chairs side by side.
To make the chairs using cones, we use a series of translations, scaling, and rotations. The following code constructs one of the legs of the chairs:
/* seat legs */ glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_green); glPushMatrix(); /* push curr matrix down stack and copy to top of stack */ glTranslatef(2.5, -2.5, 0); /* translate cylinder front and left. */ glScalef(0.15, 0.15, 1); /* scale to be narrow and tall */ gluCylinder(quadratic,1.0f,0.0f,1.0f,32,32); glPopMatrix(); /* replace curr matrix with previously pushed matrix */
The use of the matrix stack makes it easier to translate and scale from the original matrix (center). To create the chair backs the following code is used to generate two cylinders facing each other (to simulate a disc):
/* seat */ glPushMatrix(); glTranslatef(2.675, -2.675, 0.95); glScalef(0.35, 0.35, 0.10); gluCylinder(quadratic,0.0f,1.0f,1.0f,32,32); glPopMatrix(); glPushMatrix(); glTranslatef(2.675, -2.675, 1.10); glScalef(0.35, 0.35, 0.15); gluCylinder(quadratic,1.0f,0.0f,1.0f,32,32); glPopMatrix();
| Shade two different trees |
- Create and render a tree using glPushMatrix() and glPopMatrix() and your own
variation of the recursive routine provided in
http://www.gvu.gatech.edu/~jarek/courses/4451&6491/08_Transforms.ppt
- Experiment with different parameters to create trees of different kind
- Show an image with two significantly different trees side by side.
(click for larger image) |
(click for larger image) |
To generate the first (green) tree, a recursive procedure was implemented, directly from the method used in class.
/* draw a tree with an integer @rec # of recursions (branching) and using the unit
* float @length specified
* adapted from lecture slides found at:
* http://www.gvu.gatech.edu/~jarek/courses/4451&6491/08_Transforms.ppt */
void tree(int rec, float length)
{
if (rec != 0) {
/* draw cone of length of L along z-axis */
gluCylinder(quadratic,0.1f,0.0f,length*1.0f,32,32);
glPushMatrix();
glTranslatef(0.0, 0.0, 3*length/5);
glRotatef(42, 1.0, 0.0, 0.0);
glRotatef(63, 0.0, 0.0, 1.0);
glScalef(0.72, 0.72, 0.72);
tree(rec-1, length);
glPopMatrix();
glPushMatrix();
glTranslatef(0.0, 0.0, 4*length/6);
glRotatef(67, 0.0, 0.0, 1.0);
glRotatef(32, 1.0, 0.0, 0.0);
glRotatef(131, 0.0, 0.0, 1.0);
glScalef(0.91, 0.91, 0.91);
tree(rec-1, length);
glPopMatrix();
glPushMatrix();
glTranslatef(0.0, 0.0, 5*length/7);
glRotatef(-17, 0.0, 0.0, 1.0);
glRotatef(26, 1.0, 0.0, 0.0);
glRotatef(-33, 0.0, 0.0, 1.0);
glScalef(0.79, 0.79, 0.79);
tree(rec-1, length);
glPopMatrix();
}
}
The tree took in the number of recursions (indicating a branching complexity level), and a unit length to be used to create the branches at each level. To generate the green tree, a recursion level of 5 and length of 1.5 was used.
The red tree used several different values to produce a highly complex branching tree. An extra "branching" was added to the tree code (4 sets of translation, rotation, and scaling instead of 3) to generate an extra level of complexity. Also, the branch values were altered to provide a more full tree (angles increased in some areas and decreased in others). The level of recursion was upped to a level of 7, and a unit length 1.8 was used to produce the more complex tree. For full code, see the link at the bottom.
Here are both trees together, using translation to place them on the scene.
/* display trees */ glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_green); rec = 5; glPushMatrix(); glTranslatef(0, -2.5, 0); tree(rec, 1.5); glPopMatrix(); rec = 7; glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_red); glPushMatrix(); glTranslatef(0, 2.5, 0); tree2(rec, 1.8); glPopMatrix();
| Part II: Make a picture of a cone with a floor shadow |
- Make a picture of a cone with a floor shadow
For a scene with the floor plane at z=0 and light in the
direction L, the shadow, S, of a vertex, P, is given by:
.
In matrix form this is
with
![]()

- Use glMultMatrix() to multiply the current matrix with this shadow matrix
and use it to paint the floor shadow of a cone
- Write a function that calls a ShadeScene function twice, once to render the
scene and once to render its floor shadow (make sure that the shadow is in a
uniform dark color with a non-shiny surface, while the object in the scene are
displayed with their own colors.
- Demonstrate it by rendering a cylinder and its floor shadow.
- Make another image with the light in a different direction
(click for larger image) |
(click for larger image) |
(click for larger image) |
To generate the cylinder with shadow, a DrawScene() (rather than ShadeScene) function was implemented. The draw_scene() function takes in a boolean 0 or 1 to determine whether or not to shade. To draw a scene with shadows, one must call: draw_scene(0) to draw the objects, and then draw_scene(1) to draw the shadows with the transformed matrix.
/* draw_scene takes in an int to determine whether or not to draw objects
* or shadows. 0 will draw objects, 1 will draw shadows. */
void draw_scene(int shade)
{
/* variable for recursion level */
int rec = 0;
GLfloat mat_red[] = {1.0, 0.0, 0.0, 0.0};
GLfloat mat_green[] = {0.0, 1.0, 0.0, 0.0};
GLfloat mat_blue[] = {0.0, 0.0, 1.0, 0.0};
GLfloat mat_black[] = {0.0, 0.0, 0.0, 0.0};
GLfloat mat_brown[] = {0.43, 0.16, 0.08, 0.0};
GLfloat mat_dark_green[] = {0.23, 0.43, 0.21, 0.0};
if (shade == 0) {
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_blue);
}
else if (shade == 1) {
glPushMatrix();
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_black);
glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_black);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_black);
glMultMatrixf(m); /* apply shading matrix */
}
glPushMatrix();
glTranslatef(2.0, -2.0, 0);
glRotatef(20, 0, 0, 1);
draw_chair1();
glPopMatrix();
if (shade == 0) {
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_red);
}
/* code snip - draw more objects, trees, chairs, etc. */
if (shade == 1) {
glPopMatrix();
}
}
Note the use of the glMultMatrix(m) that multiplies the current matrix by the shadow matrix, m:
/* this is the shadow matrix. It has been implemented correctly.
* OpenGL has a funky ordering for rows and columns
* use this ordering for rows and columns. The identity matrix with Mr,c = M3,3 = 0;
*/
m[0]= 1; m[4]= 0; m[8] = -(light1_x/light1_z); m[12]= 0;
m[1]= 0; m[5]= 1; m[9] = -(light1_y/light1_z); m[13]= 0;
m[2]= 0; m[6]= 0; m[10]= 0; m[14]= 0;
m[3]= 0; m[7]= 0; m[11]= 0; m[15]= 1;
The images above are made using a light source at (10, 10, 10), then at (0, 45, 10), and (13, -25, 10) respectively.
| Floor tiles texture |
- Use a large polygon on the floor and a checkerboard texture (of your
choice) to create the impression of tiles on the floor.
- Make sure that the tiles are slightly under the shadows.
Using this image:
For full code to implement a textured image, see http://www2.york.ac.uk/services/cserv/sw/graphics/OPENGL/L16.c. This link is to a class website on graphics, with similar work on textured images. Basically, the image is loaded using the BMPloader.c code, also provided and credited below, as a texture map. The code necessary is that to load the texture and enable it using glEnable(GL_TEXTURE_2D), and to draw the coordinates with the glTexCoord2f(x, y) before drawing the vertices of our quad for the floor.
/* display tiles */
/* code provided by Rob Fletcher 2001
* http://www2.york.ac.uk/services/cserv/sw/graphics/OPENGL/L16.c
*/
GLvoid LoadTexture(GLvoid)
{
/* code snip */
}
The code to implement the textured floor without a bmp texture image was already provided in the p3 sample code. See below for links.
| Put It All Together |
- Make a garden scene with some trees and some chairs arranged in patterns
- Use the tiles floor
- Add shadows for everything
- Make several images showing how the shadows vary throughout the day
(click for larger image) |
(click for larger image) |
(click for larger image) |
(click for larger image) |
The entire scene is simply rendered in the draw_scene() procedure as series of calls to functions that draw objects. The final draw_scene() function is as follows:
/* draw_scene takes in an int to determine whether or not to draw objects
* or shadows. 0 will draw objects, 1 will draw shadows. */
void draw_scene(int shade)
{
/* variable for recursion level */
int rec = 0;
GLfloat mat_red[] = {1.0, 0.0, 0.0, 0.0};
GLfloat mat_green[] = {0.0, 1.0, 0.0, 0.0};
GLfloat mat_blue[] = {0.0, 0.0, 1.0, 0.0};
GLfloat mat_black[] = {0.0, 0.0, 0.0, 0.0};
GLfloat mat_brown[] = {0.43, 0.16, 0.08, 0.0};
GLfloat mat_dark_green[] = {0.23, 0.43, 0.21, 0.0};
if (shade == 0) {
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_blue);
}
else if (shade == 1) {
glPushMatrix();
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_black);
glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_black);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_black);
glMultMatrixf(m); /* apply shading matrix */
}
glPushMatrix();
glTranslatef(2.0, -2.0, 0);
glRotatef(20, 0, 0, 1);
draw_chair1();
glPopMatrix();
if (shade == 0) {
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_red);
}
glPushMatrix();
glTranslatef(-1.75, 0.25, 0);
glRotatef(-20, 0, 0, 1);
draw_chair2();
glPopMatrix();
if (shade == 0) {
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_green);
}
glPushMatrix();
glTranslatef(-1.0, -2.0, 0);
glRotatef(40, 0, 0, 1);
draw_chair2();
glPopMatrix();
if (shade == 0) {
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_green);
}
glPushMatrix();
glTranslatef(-1.75, -2.0, 0);
glRotatef(-50, 0, 0, 1);
draw_chair1();
glPopMatrix();
if (shade == 0) {
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_brown);
}
rec = 7;
glPushMatrix();
glTranslatef(0, -0.5, 0);
tree2(rec, 1.8);
glPopMatrix();
if (shade == 0) {
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_dark_green);
}
rec = 6;
glPushMatrix();
glTranslatef(-3.0, 3.0, 0);
tree(rec, 1.1);
glPopMatrix();
if (shade == 0) {
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_dark_green);
}
rec = 7;
glPushMatrix();
glTranslatef(-3.0, -3.5, 0);
tree(rec, 1.5);
glPopMatrix();
if (shade == 0) {
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_brown);
}
rec = 6;
glPushMatrix();
glTranslatef(3.0, -3.0, 0);
glRotatef(-90, 0, 0, 1);
tree(rec, 1.3);
glPopMatrix();
if (shade == 0) {
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_dark_green);
}
rec = 6;
glPushMatrix();
glTranslatef(3.0, 3.0, 0);
glRotatef(90, 0, 0, 1);
tree(rec, 1.5);
glPopMatrix();
if (shade == 1) {
glPopMatrix();
}
The images above show the light source at different directions to simulate different times of the day. One shows late sunset, another with sun at high noon (shadow straight down) and others with the light source in between.
| View Control |
- Make 3 images of the garden from different directions (no roll).
(click for larger image) |
(click for larger image) |
(click for larger image) |
(click for larger image) |
To generate the garden views, I implemented a form of view control. I also implemented a way to change the lighting direction.
To move the light source while the program is running, one can use:
w and s to move the light source's x value up and down.
a and d to move the light source's y value up and down.
To move the view, in this case the eye_point, one can use:
j and l to move the eye point's x value up and down.
i and k to move the eye point's y value up and down.
u and o to move the eye point's z value up and down.
This allows one to generate all sorts of scenes from inside the program. One can increase the y value of the light to make it look like a sunset, while moving the view towards the west to see how the trees become darker on the side closer to the shadows. The code to change the values was placed inside the keyboard GLUT function, and the glutPostRedisplay() called at the end. The eyepoint code is:
/* multiplies the current matrix by a tranformation matrix that puts the eyepoint
* at , center of the image at <0,0,0>, and up vector of <0,0,1> (z-axis). */
gluLookAt(eye_x,eye_y,eye_z, 0,0,0, 0,0,1);
Unfortuantely, the complexity of the scene makes it hard to do smooth rendering, upon multiple view changes, it takes a few seconds to render. More limitations are discussed below.
| Limitations / Unrealistic Aspects |
The goal of computer graphics is to generate realistic looking scenes quickly and efficiently. One of the main goals is to mimic the real world, and we do this by applying all sorts of neat tricks, like shadows, lights, textures, and complex objects. In this project, a complex garden scene was created, however, the scene contains some limitations that make it seem unrealistic.
The objects themselves were created using cones and cylinders. Objects in the real world may often resemble spheres, boxes, and even more complex objects. Using cones to render all objects will greatly reduce realistic image production. Even using an object like a caplet allows for some complexity, but still objects exist that are not comprised of basic shapes--they themselves are basic shapes. The cones work well to generate trees, however, the trees themselves have no real collision detection. The branches on greater recursion levels and complexity levels actually collide with each other and grow into each other. This does not happen in the real world. Objects may also collide, without repercussions. Chairs may hit backs, or other objects and trees.
The main limitation is that of the shadows only on the floor. Our current method for implementing the shadows only generates a shadow on the floor, not on other objects. It does not take into account any other objects combination of shadows, but only generates every shadow as a black texture on the floor. Also, the shadow is only from one light source. We cannot generate multiple transparent shadows from many angles with the current code and implementation. We could add the ability to detect if a shadow falls on a surface, or we could simply use a different form of shadow calculation. We could handle all the cases where a shadow fell on a object by using detection. For instance, we could generate our shadow and compare it to all the objects we have created. We could then use a form of texture application to let the object display the shadow. As of now, the code cannot be made to display shadows on objects easily.
Other unrealistic aspects are the use of single colors and textures, and the
apparent floating achieved by only displaying a floor. No background or
sky was used, so the objects have a distant "floating" aspect that implies they
are very unrealistic. The trees are also somewhat uniform, and do not show
signs of "randomness," what we associate with nature. No two trees in
nature look alike, yet all of ours differ only in rotation, translation,
recursion/complexity levels, and sizes of branches.
| Full Source Code |
Makefile
p3.c
BMPloader.c
Quinn_Charles_3.tar - archive of all files
including pictures
Date Posted / Last Updated: Tuesday, 29 October 2002, 2:55 EST
by Charles Brian Quinn gte430p@mail.gatech.edu