# Project 2: 3D Models and Lighting

In this homework assignment you're going to practice how to:

• render 3D models in modern OpenGL
• control objects using the mouse
• set up light sources and material properties

Watch a demo of last year's project that includes some extra credit work, but differs in some of the detail from this year's project.

Note that before the lecture on Oct 16 you should be able to solve parts 1, 2, and much of 5 (position and representation). On Oct 16 we'll be covering the material for the remaining parts of the project.

## 1. Rendering using modern OpenGL (5 Points)

Download and build the starter code for project 2, which renders a spinning cube with modern OpenGL, through the use of a VAO (Vertex Array Object), VBOs (Vertex Buffer Object), and EBOs (element buffer object). In order to make the use of the programmable pipeline possible, a shader class was added to the starter code, along with a sample vertex and fragment shader. At the moment, the sample fragment shader causes all objects to be colored pink. You will be fixing this later when you begin working with lights.

Notes:

• This is a good resource for learning modern OpenGL.

## 2. 3D Model Loader (20 Points)

You already know how to load point clouds. It turns out that the 3D model files from project 1 (Bunny, Bear, Dragon) contain surface descriptions as well, by means of triangle connectivity.

Besides vertices and vertex normals you are going to have to parse the files for connectivity. It is defined with the letter 'f' for face. Each line starting with the letter 'f' lists three sets of indices to vertices and normals, which define the three corners of a triangle. The numbers are indices into the vertex and vertex normal lists. Note that the indices are 1-based (start with index 1). Example:
`f 31514//31514 31465//31465 31464//31464`

Copy your OBJ loader code from project 1 into the new project, and add parsing the 'f' lines to load triangles. Then modify your code to display the OBJ models with triangles instead of the cube. Allow switching between them just like in project 1 with the F1, F2 and F3 keys.

Make sure you copy your centering and resizing code from project 1 so that the models more or less fill your graphics window.

## 3. Mouse control (25 Points)

If you still have your spin-while-idle code, now is the time to disable it. From now on we support the mouse to control your 3D models. Add functionality to allow the following operations on the displayed 3D model:

### Scaling the model (10 Points)

Support the mouse wheel to scale the model up or down, using matrix transformations (not by modifying each of the vertex positions like in project 1). If you don't have a mouse wheel, use the right mouse button. If you don't have that, use two-finger drags on the touch pad. If your computer doesn't support that either, use the 's'/'S' keys to scale.

### Rotating the model (15 Points)

While the left mouse button is pressed and the mouse is moved, rotate the model about the center of your graphics window. We will refer to this operation as "trackball rotation". This video shows how this should work.

The figure below illustrates how to translate mouse movement into a rotation axis and angle. m0 and m1 are consecutive 2D mouse positions. These positions define two locations v and w on an invisible 3D sphere that fills the rendering window. Use their cross product as the rotation axis a = v x w, and the angle between v and w as the rotation angle.

Horizontal mouse movement exactly in the middle of the window should result in a rotation just around the y-axis. Vertical mouse movement exactly in the middle of the window should result in a rotation just around the x-axis. Mouse movements in other areas and directions should result in rotations about an axis a which is not parallel to any single coordinate axis, and is determined by the direction the mouse is moved in.

Once you have calculated the trackball rotation matrix for a mouse drag, you will need to multiply it with the object-to-world transformation matrix of the object you are rotating.

For step by step instructions, take a look at this tutorial. Note that the tutorial was written for Windows messages, instead of GLFW mouse events. This means that you'll need to replace the "CSierpinskiSolidsView::OnLButtonDown", "CSierpinskiSolidsView::OnMouseMove", etc. with an appropriate GLFW equivalent.

To help you understand the code better, here is a line-by-line commented version of the trackBallMapping function:

```Vec3f CSierpinskiSolidsView::trackBallMapping(CPoint point)    // The CPoint class is a specific Windows class. Either use separate x and y values for the mouse location, or use a Vector3 in which you ignore the z coordinate.
{
Vec3f v;    // Vector v is the synthesized 3D position of the mouse location on the trackball
float d;     // this is the depth of the mouse location: the delta between the plane through the center of the trackball and the z position of the mouse
v.x = (2.0*point.x - windowSize.x) / windowSize.x;   // this calculates the mouse X position in trackball coordinates, which range from -1 to +1
v.y = (windowSize.y - 2.0*point.y) / windowSize.y;   // this does the equivalent to the above for the mouse Y position
v.z = 0.0;   // initially the mouse z position is set to zero, but this will change below
d = v.Length();    // this is the distance from the trackball's origin to the mouse location, without considering depth (=in the plane of the trackball's origin)
d = (d<1.0) ? d : 1.0;   // this limits d to values of 1.0 or less to avoid square roots of negative values in the following line
v.z = sqrtf(1.001 - d*d);  // this calculates the Z coordinate of the mouse position on the trackball, based on Pythagoras: v.z*v.z + d*d = 1*1
v.Normalize(); // Still need to normalize, since we only capped d, not v.
return v;  // return the mouse location on the surface of the trackball
}
```

## 4. Define Lights and Materials (20 Points)

In order to render the 3D models as realistically as possible, write shaders to render them with the Phong illumination model and per pixel lighting. Per pixel lighting means that you have to do all of your light reflection calculations in the fragment shader. For the shader to work, you will have to define light sources as well as different materials for each of your 3D models. Write vertex and fragment shaders to support the features listed below. In this part of the project, you should use fixed positions for the lights and all other lighting parameters. But know that in part 5 you're going to have to make some of these parameters user modifiable, so you may want to already introduce variables instead of using constant values.

### Materials (10 Points)

Assign each of the 3D model files different material properties, following the instructions below. Each object should have a different color of your choice.

• One of the models (your pick) should be very shiny (ie, high specular component), with no diffuse reflection.
• Another model should only use diffuse reflection, with zero shininess.
• The third object should have significant diffuse and specular reflection components.

Support keyboard key 'n' to switch between the rendering modes of normal coloring and your new Phong illumination model.

• Normal coloring is useful to keep around so that you can check if your surface normals have been calculated correctly. Normal coloring should work just as you implemented it in project 1, except now you render the entire 3D model (all triangles, not just the vertices) with normal shading.
• In Phong render mode, you render your 3D models more realistically, determined by their materials and the type of light source shining on them.

### Light sources (10 Points)

Create two different light sources: a point light and a spotlight. Use C language structs to define their properties, as given below. Use linear attenuation when calculating the light intensity.

#### Point light properties

• Color (vec3)
• Position (vec3)

#### Spotlight properties

• Color (vec3)
• Position (vec3)
• Direction of center of cone (vec3)
• Spot cutoff (float)
• Spot exponent (float)

## 5. Interactive Light Controls (30 Points)

Each light should have a different color of your choice. To test out the effect of your light sources, make the light sources movable with the mouse (rotate them around the model with your trackball controls), and add keyboard commands for certain illumination parameters as described below.

#### Light Position (10 Points)

Select which lights are affected by rotations: '1' toggles (enables/disables) point light rotation, '2' toggles spotlight rotation on/off. When they are on, the light should rotate around the model when the mouse is moved. When they are off, the light should be fixed in model space, ie, it should rotate with the model. The '0' key should toggle model rotations on/off. Both lights should be illuminated at all times.

The operation you implemented to scale the model (ie, mouse wheel), should change the distance of the light from the center of the graphics window.

#### Light Representation (10 Points)

For both light sources, display this sphere in the position of the light source and in its color.

The sphere needs to be scaled down to be only about 1/4 inch wide on the screen, just big enough to be clearly visible. It's best to scale them down with a uniform scale matrix just before the model matrix transformation (or as part of the model matrix).

To draw the sphere in the color of the light source you should use the light color as the light's ambient reflection value, and use no diffuse or specular reflection (they wouldn't work properly because the light source is inside of the light source geometry).

#### Spotlight Parameters (10 Points)

• 'w'/'W' should make the spot wider/narrower.
• 'e'/'E' should make the spot edge sharper/blurrier.
• The spot should always point at the center of your graphics window as it rotates around the model.

#### Notes

• This video demonstrates what is expected (but it does not include normal coloring).
• Learn OpenGL provides vertex and fragment shader code, as well as the corresponding C++ code for different lighting parameters. The shader does almost exactly what you need. Find out how it differs from the equations given on the discussion slides for this homework project and make the few modifications.
• Lighthouse 3D also provides excellent tutorials for the necessary shaders: for directional lights, point lights and spot lights.
• This tutorial provides very useful information on light parameters in chapters 6 and 7. An additional tutorial on different light types is provided in chapter 8.
• This table of material properties may inspire you to select interesting material parameters. But note that there are specific requirements for the materials you use, which make it necessary to eliminate one or more reflection components (ambient, diffuse, specular) when you define your own materials.

• -5 if all that's done is passing the normals
• -10 if all code is there but lighting doesn't work

## 6. Extra Credit (10 Points)

You can receive up to 10 points of extra credit if you do one or more of the following things:

1) Add a directional light source and make its direction controllable with your virtual trackball. (3 points)

2) Add support to turn on or off each light source individually. (3 points)

3) Create a visual editor to allow the custom modification of the colors and shading parameters of your light source and surface materials. Use the 'c' key to display the color editor. The editor needs to be exclusively mouse controlled. You can either write your own OpenGL code to generate GUI widgets such as buttons or sliders, or you can use an existing open source library for it, such as NanoGUI.

You'll get points for the modification of the following parameters:

• The object's colors and other parameters for ambient, diffuse and specular reflectivity. (5 Points)
• The light's color and other properties: if using a spot light: cone angle, edge falloff; else: attenuation mode (constant, linear, quadratic). (5 Points)