Project5SP15

From Immersive Visualization Lab Wiki
Jump to: navigation, search

Contents

Project 5: Android Army

In this project you will practice the texturing of geometry, and you will implement your own scene graph, in order to render an army of androids.

You can start out with your code from project 4. Don't worry if your mouse routines lights don't work perfectly, neither is required for this homework project.

The project is due on Friday, May 8th, 2015 at 1:00pm. You need to present your results in the CSE basement labs as usual, grading starts at 12:15pm.

The homework discussion for this project will be on Monday, May 4th.

The total score for this project is 100 points, plus 10 for extra credit.

1. Sky Box (20 Points)

Create a sky box for your androids to have a space to live in. A sky box is a large box which is drawn around your entire scene and which your scene is inside of. The walls of the box typically have pictures of a sky with clouds, objects in the distance, and the ground below the camera. But they can just as soon represent the interior of a building. Perhaps an alien spaceship?

Sky boxes are typically cubic, which means that they consist of six square textures for the six sides of a cube. Here is is a nice collection of textures for sky boxes, but there are many others all over the internet.

Draw a cubic sky box by generating six sides of a cube out of large textured GL_QUADS. Position and scale the box so that its center is in the origin, and the walls of the cube are about 100 times farther apart than the width of your scene. Put the camera inside the sky box (so that you can see what is inside of it).

Use the following settings for your texture after your first glBindTexture for correct lighting and filtering settings:

  // Make sure no bytes are padded:
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

  // Select GL_MODULATE to mix texture with polygon color for shading:
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

  // Use bilinear interpolation:
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

To familiarize yourself with texture mapping in OpenGL, we provide a sample program, which loads a PPM file and uses it as a texture for a quad. If you decide to use one of the above referenced sky box images, you will have to convert them from JPEG to PPM format. The free image processing tool IrfanView for Windows will do this for you. For Linux or Mac, we recommend ImageMagick.

If you want to load image formats other than PPMs (e.g., JPEG or TIFF) directly into your application, link your code against a third party library such as SOIL.

Grading:

  • -5 if the same texture shows up on all sides of the sky box
  • -5 if less than six texture files are loaded
  • -5 if all textures are loaded and displayed but they do not match up at the edges

2. Android Head (15 Points)

Now it is time to create the head of an android. The first step is to create the geometry for it, along with texture coordinates. The easiest way to do this is to create a sphere or cylinder. This web site explains the algorithms and gives pseudo-code. Make sure you assign appropriate texture coordinates to each vertex, as well as normals. Refer to the course slides for how to generate texture coordinates for a sphere or cylinder.

Androids' faces typically look human-like. So we are going to use somebody's head shot as the texture. [www.cse.ucsd.edu/faculty_profile Here] is a good starting point for images, but you are free to use any image you would like.

3. Scene Graph Engine (20 Points)

To create the remaining body parts of the android (torso, arms, legs), we need to first implement a scene graph structure for our rendering engine. Use the following hierarchy:

Project4F14-scenegraph-half.jpg

The classes should have at least the following functionality:

  • Class Node should be abstract and serve as the common base class. It should implement an abstract draw method: virtual void draw(Matrix4 C) = 0, and also an abstract virtual void update() = 0 method to separate bounding sphere updates from rendering.
  • Group should store a list of pointers to child nodes (std::list<Node*>) and provide functionality to add and remove child nodes (addChild(), removeChild()). Its draw method needs to traverse the list of children and call each child node's draw function.
  • Geode should be an abstract class. It should set OpenGL's ModelView matrix to the current C matrix, and have an abstract render function to render its geometry.
  • MatrixTransform should store a 4x4 transformation matrix M which is multiplied with matrix C, which is passed to the draw method.
  • Sphere should have a draw function which draws a sphere. You can use glutSolidSphere for that, or use your own tessellation algorithm.
  • Cube should have a draw function which draws a cube. You can use glutSolidCube for that, or use your own implementation of a cube.

Note:

  • If you created your android's head with a cylinder, you can replace the Cube class with your Cylinder class.

4. Walking Android (30 Points)

Now we are going to finish our android's geometry. First get your rendering engine ready to recursively traverse the scene graph for rendering by creating a root node of type Group and calling its draw() function with the identity matrix as its parameter.

Besides the head, the android should have a torso, arms and legs. This means that it has to consist of at least 6 instances of Geode-derivatives. You can use the sphere and cube classes for the body parts, and add more shapes as you like (e.g., using any of GLUT's pre-defined shapes). Each Geode-derivative should have a MatrixTransform before it to position the body part(s) below it in the scene graph. You might want some of your basic shapes, such as spheres and cubes, elongated, rotated, or otherwise deformed, which you should do with separate MatrixTransform nodes for (non-uniform) scales, and/or rotations. It is very common to concatenate multiple MatrixTransform nodes in a scene graph, each of which doing a separate affine transformation (e.g., one for position, one for scale, one to rotate - each separate MatrixTransforms).

Animate the android to make it look like it is walking: move arms and legs back and forth by rotating around hip and shoulder joints. You can do this by re-calculating rotation matrices every frame by increasing or decreasing the respective angle by a small amount, up to a limit point at which you reverse the direction.

5. Android Army (15 Points)

Test your implementation by constructing a scene which consists of a large amount of androids, at least 100. The androids can all be identical clones, or you can use different pictures for their heads, or even vary the geometry.

  • Distribute the androids on a 2D grid (i.e., place them on a plane with uniform spacing). For 100 androids, use a 10x10 grid of them.
  • Enable the animation for your androids so that they look like they are walking.
  • Enable your rotation, pan and scale routines (keyboard or mouse) to allow the user to rotate the grid of 3D objects and zoom in or out.

This image illustrates the grid layout of the robots:

Robots.png

6. Extra Credit (10 Points)

There are two options for extra credit. Each of them gets you 10 points. (You cannot get more than 10 points total even if you do both.)

A. Optimization by Culling

The first option is to implement object level culling, to allow the existence of thousands of instances of your android, without having to render them all.

Calculate a bounding sphere (Vector3 for its center point, and a radius) for each of your androids, which contains all parts of the robot. Allow the display of the bounding spheres by rendering them as wireframe spheres with glutWireSphere. Enable a keyboard key to toggle the bounding spheres on and off.

Note: You do not need to find the tightest possible bounding spheres - that can be rather difficult. Just make them as tight as you reasonably can.

Add view frustum culling using the bounding spheres of the objects. If the bounding sphere of an object is completely outside of the view frustum, the object should be culled (not rendered). Your culling algorithm should make use of a utility function to test whether a bounding sphere intersects with a given plane (the planes of the view frustum), or whether the sphere is entirely on one side of the plane.

Enable a keyboard key to turn culling on and off.

Increase the amount of androids by orders of magnitude, by creating a larger array of them. A good portion of the array should be off screen for the culling to be effective. Display the rendering time per frame in your text window, and show that by turning culling on your rendering time decreases.

B. Texturing of 3D Models

This option allows you to practice how to texture more complex 3D models. Each of the two models gets you 5 points.

Cow

Load this 3D model file of a cow.

Texture the cow with this texture. The cow model file does not come with texture coordinates, so you will need to generate them yourself. Use the following approach:

Calculate the 3D location of the center of the cow: find the minimum and maximum x, y and z coordinates across all vertices (i.e., the edges of the tightest axis parallel bounding box), and calculate its center point. This is the center of the sphere which you should shrink-wrap onto the cow's surface, as covered in class.

Teapot

Load this 3D model of a teapot. Note that it comes with texture coordinates, so you will need to extend your OBJ reader code to parse texture coordinates.

The goal is to texture the teapot with this UCSD logo. The goal is to show the logo only on the kettle part of the teapot. When you first load it in using the texture coordinates from the file, the logo will cover the entire teapot including handle and spout, and it will be in the wrong place.

To get it into a more reasonable place, i.e., on the side of the teapot, you will need to apply an affine transformation to the texture coordinates you pass on to OpenGL. The best place to do this might be just before you pass them on to OpenGL between glBegin and glEnd.

Note that you should use glTexParameter GL_CLAMP_TO_EDGE, so that the logo's edge color (white) fills the rest of the teapot.