Project 2: 3D Models and Lighting
In this homework assignment you're going to practice how to:
- load OBJ files with polygons (not just points)
- render 3D models with triangles
- control objects with the mouse
- set up light sources and material properties
Watch a demo of a previous year's homework project that includes some extra credit work, but differs in some of the detail from this year's project.
Start with your code from project 1. We're going to build on top of it for this project. You don't need to retain the point rendering functionality.
- This is a good resource for learning modern OpenGL.
1. 3D Model Loader (20 Points)
You already know how to load point clouds. 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 (lines starting with 'vn'). Note that the indices are 1-based (start with index 1). Example:
f 31514//31514 31465//31465 31464//31464
To your parser from project 1 add parsing the 'vn' lines to load normals, and 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 (or keys of your choice).
Make sure you copy your centering and resizing code from project 1 so that the models approximately fill your graphics window.
2. Mouse Control (25 Points)
Disable or remove your code that spins the models automatically. From now on you need to support the mouse to control your 3D models. Add functionality to allow the following operations on the displayed 3D model:
Scaling the model (15 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 shift key along with the mouse button 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:
3. 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)
- Color (vec3)
- Position (vec3)
- Direction of center of cone (vec3)
- Spot cutoff (float)
- Spot exponent (float)
- -5 if all that's done is passing the normals
- -10 if all code is there but lighting doesn't work
4. 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.
- -3 if the control of lighting works in a sense but does not match the description.
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.
- 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. 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)