Difference between revisions of "Project5Fall14"

From Immersive Visualization Lab Wiki
Jump to: navigation, search
(1. 3D Model Loader)
(4. Add Per-Pixel Lighting (20 Points))
 
(28 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
=Project 5: Light and Shade=
 
=Project 5: Light and Shade=
 
'''THIS PAGE IS UNDER CONSTRUCTION - PLEASE DO NOT YET START WITH THIS PROJECT'''
 
  
 
This project is on OpenGL lighting and shading, and includes GLSL shader programs.  
 
This project is on OpenGL lighting and shading, and includes GLSL shader programs.  
Line 11: Line 9:
 
* This project does not require a scene graph data structure. But we encourage you to use your scene graph for this and the remaining projects because it can make your code easier to write and debug.
 
* This project does not require a scene graph data structure. But we encourage you to use your scene graph for this and the remaining projects because it can make your code easier to write and debug.
  
==1. 3D Model Loader==
+
==1. 3D Model Loader (20 Points)==
  
 
OBJ files are 3D model files which store the shape of an object with a number of vertices, associated vertex normals, and the required connectivity information to form triangles. [http://en.wikipedia.org/wiki/Wavefront_.obj_file Wikipedia] has an excellent page on the OBJ file format. The file format is an ASCII text format, which means that the files can be viewed and edited with a text editor.
 
OBJ files are 3D model files which store the shape of an object with a number of vertices, associated vertex normals, and the required connectivity information to form triangles. [http://en.wikipedia.org/wiki/Wavefront_.obj_file Wikipedia] has an excellent page on the OBJ file format. The file format is an ASCII text format, which means that the files can be viewed and edited with a text editor.
  
Here are three sample files:
+
Here are three files for you to work with:
 +
 
 +
* [[Media:BunnyF14.zip|Bunny ]]
 +
* [http://www.calit2.net/~jschulze/tmp/DragonF14.zip Dragon]
 +
* [http://www.calit2.net/~jschulze/tmp/BearF14.zip Bear]
  
 
The files provided in this project only use the following three data types (other OBJ files may support more):
 
The files provided in this project only use the following three data types (other OBJ files may support more):
Line 45: Line 47:
 
</pre>
 
</pre>
  
==1. Mouse Control (20 Points)==
+
Once you loaded your model file, apply your full-screen algorithm from project 2.2 to scale the data sets up or down to match the screen size, and render the model. Use your rotation and scale routines to spin the model around and zoom in.  
  
Start by reviving your application from assignment 2: you will need to be able to display the cube, as well as the following three 3D models into your application and display them at reasonable sizes (screen filling). The function keys should switch between the models.
+
Grading:
 +
* -5 if only one data set loads
 +
* -5 if normals aren't correct
 +
* -5 if triangle mesh is incorrect
  
These are the models you need to be able to load, you will have to unzip them to get at the OBJ files. Note that some of them are different files than before, as we now need files with normals. The OBJ reader we gave you in project 2 already parses normals.
+
==2. Mouse Control (30 Points)==
  
* [[Media:dragon.zip|Dragon ]]
+
Now it is time to improve on those keyboard commands for rotations of your 3D model. Add a mouse control function, to allow rotating the model about the center of your OpenGL window, as well as scaling the model up and down. The '''left mouse button should be used for rotation''', the '''right mouse button should zoom in and out''' when the mouse is moved up or down while it is pressed.  
* [[Media:bunny_n.zip|Bunny ]]
+
* [[Media:sandal.zip|Sandal ]]
+
  
You are welcome but not required to support the keyboard commands from assignment 2 to move around the models. However, you will need to add mouse control, which will allow rotating the model about the center of your OpenGL window, as well as scaling the model up and down. The '''left mouse button should be used for rotation''', the '''right mouse button should zoom in and out''' when the mouse is moved up or down while it is pressed.  
+
[[Media:cube.mpg|This video]] shows how the trackball-like rotation should work.  
  
[[Media:cube.mpg|This video]] shows how the trackball-like rotation should work. We provide [[Media:trackball-code.txt|sample code for the trackball rotation.]] You will need to adapt it to the syntax of your matrix and vector classes and get it to work within your application.
+
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.
 
+
If you cannot get the sample trackball code to work, the following description of the algorithm might help you.
+
 
+
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 a 3D virtual 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.
+
  
 
[[Image:Trackball.jpg]]
 
[[Image:Trackball.jpg]]
Line 67: Line 66:
 
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.
 
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 for the model or the light source you are moving.
+
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.
  
 
To access the mouse x and y coordinates, you will need to use GLUT's callback functions [http://www.opengl.org/resources/libraries/glut/spec3/node50.html glutMouseFunc()], which gets called when you press a mouse button, and [http://www.opengl.org/resources/libraries/glut/spec3/node51.html glutMotionFunc()], which gets called constantly while you hold the button down and move the mouse. Note that successive trackball rotations must build on previous ones; at no point should the model snap back to a previous or default position.
 
To access the mouse x and y coordinates, you will need to use GLUT's callback functions [http://www.opengl.org/resources/libraries/glut/spec3/node50.html glutMouseFunc()], which gets called when you press a mouse button, and [http://www.opengl.org/resources/libraries/glut/spec3/node51.html glutMotionFunc()], which gets called constantly while you hold the button down and move the mouse. Note that successive trackball rotations must build on previous ones; at no point should the model snap back to a previous or default position.
  
==2. OpenGL Lighting and Shading (40 points)==
+
For step by step instructions, take a look at [http://web.cse.ohio-state.edu/~crawfis/Graphics/VirtualTrackball.html this tutorial]. Note that the tutorial was written for Windows messages, instead of GLUT mouse functions. This means that you'll need to replace the "CSierpinskiSolidsView::OnLButtonDown" function with "glutMouseFunc", "CSierpinskiSolidsView::OnMouseMove" with "glutMotionFunc", etc.
 +
To help you understand the code here is a line-by-line commented version of the trackBallMapping function:
 +
 
 +
<pre>
 +
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
 +
}
 +
</pre>
 +
 
 +
Grading:
 +
* -5 if rotation is not in world coordinates
 +
* -5 if object's size changes when rotating
 +
* -10 if there is no zoom
 +
* -5 if rotation is in opposite direction
 +
* -10 if rotation is not cumulative
 +
 
 +
==3. Fixed Function Lighting (30 points)==
  
 
Write classes to manage light and material properties (<tt>Light</tt> and <tt>Material</tt>). As a starting point, refer to the relevant sections in [http://www.glprogramming.com/red/chapter05.html Chapter 5 of the OpenGL Programming Guide], as well as the [http://www.opengl.org/archives/resources/faq/technical/lights.htm OpenGL Lighting FAQ].
 
Write classes to manage light and material properties (<tt>Light</tt> and <tt>Material</tt>). As a starting point, refer to the relevant sections in [http://www.glprogramming.com/red/chapter05.html Chapter 5 of the OpenGL Programming Guide], as well as the [http://www.opengl.org/archives/resources/faq/technical/lights.htm OpenGL Lighting FAQ].
  
Associate material properties with each of the four 3D model files: two of them should be shiny, and the other two should be shiny and diffuse - you choose which. Use colors other than white. ('''10 points''')
+
Associate different material properties with each of the 3D model files: make one more shiny, another one more diffuse. Use colors other than white. ('''15 points''')
  
Create three light sources: one directional light, one point light and one spot light. The spot light should always point towards the center of the OpenGL window. Give them initial positions and colors. Each of them must have a different position and color. The spot light should have a spot width narrow enough so that it only illuminates a small part of the surface of the models. Toggle each of them on and off with one of the number keys: 1, 2, and 3. Rotate those light sources which are on with the 3D model.  
+
Create two light sources: a point light and a spot light. Give them initial colors, positions, directions and opening angles as applicable. The spot light should have a spot width narrow enough so that it only illuminates a small part of the surface of the models ('''10 points'''). Draw a glutSolidSphere in the location of the point light ('''2 points'''), and a glutSolidCone in the location of the spot light, the wide end pointing in the direction of the spot ('''3 points'''). Make sure the light source positions are chosen so that both are visible on the screen. It may help to scale down the models some so that there is a longer distance between lights and model.
  
Add a freeze mode for the 3D model and toggle it with the 'm' key: when it is enabled, the model will not respond to mouse control commands and instead stay in its last orientation and size. In this mode only the enabled light sources rotate and zoom in and out.
+
The lights should be stationary in world space: when the 3D model is rotated, the lights should not rotate with it.
  
 
'''Notes:'''  
 
'''Notes:'''  
  
* OpenGL multiplies light position and direction with the Modelview matrix when they are set. Therefore, you need to modify the Modelview matrix with your mouse control routines to rotate the light sources, independently for each light source.
 
 
* To ascertain that the normals of your 3D models will survive zoom operations correctly, you should use the following OpenGL command: <tt>glEnable(GL_NORMALIZE)</tt>.
 
* To ascertain that the normals of your 3D models will survive zoom operations correctly, you should use the following OpenGL command: <tt>glEnable(GL_NORMALIZE)</tt>.
* By default, OpenGL uses a simplified model for the calculation of the highlights. For a more realistic model add this command to your code: <tt>glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE)</tt>.
+
* By default, OpenGL uses a simplified model for the calculation of the highlights. For a more realistic model add this command to your main() function: <tt>glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE)</tt>.
  
==3. GLSL Shader Programs (40 Points)==
+
==4. Add Per-Pixel Lighting (20 Points)==
  
Imagine that each of your light sources has a force field, which attracts or repulses the vertices of your geometry, and shifts their color.  
+
Up until now, the spot light's outline on the surface of the 3D models has been rather fuzzy. Add per pixel lighting for the spot light to your application. To accomplish this, you need to add a vertex and a fragment shader to your application and enable them. In this project, you are free to copy-paste the shader code into your application from a source of your choice, for instance [http://www.lighthouse3d.com/tutorials/glsl-tutorial/spot-light-per-pixel/ Lighthouse3D]. You do not need to understand the shader code, just make it work.
  
* The directional light should repulse the vertices, and do not cause a color shift.
+
Toggle this per-pixel lighting mode on and off with the 'p' key. When it is enabled, you do not need to render the effect of the point light, because this would require modifying the shader.
* The point light should repulse the vertices as well, and shift their color towards red.
+
* The spot light should attract the vertices, but only if they fall within its spot, and shift their color towards green.
+
  
In each case, the attraction or repulsion should happen in the direction of the light (using the light vector), and should attenuate with the square distance from the light source, so that those vertices closer to the light source are affected more than those farther away. Note that the directional light does not have a position - implement the effect so that only polygons facing the light source are affected by it (by comparing the surface normals to the incoming light vector). Implement this effect as a GLSL vertex shader.
+
For Windows and Linux users, in order to use the OpenGL extensions for shaders, you should [https://www.opengl.org/sdk/libs/GLee/ download GLee] and add the glee.h and glee.c files to your project files, or tell the linker to link with the GLee library (glee.lib or glee.dll for Windows, or libglee.a/libglee.so for Linux). OSX users will not need GLee, as the OpenGL extensions are available by default. In case the GLee server is down, you can find the files [[Media:GLee5_4.zip | in this ZIP file.]]
 
+
The color shift should be done by about 10%, and must be strong enough so that it is obvious. The color shift '''must''' be implemented on a per pixel basis, which means that you will need to implement it as a GLSL fragment shader.
+
 
+
You will need to demonstrate the described effect for the 3D objects you used in the prior homework projects: specifically: cube, dragon, bunny and head. Be sure to make your objects big enough that they almost fill the OpenGL window, either by using your code from project 2, or by using fixed scale factors.
+
 
+
For Windows and Linux users, in order to use OpenGL extensions, you should [http://elf-stone.com/glee.php download GLee] and add the glee.h and glee.c files to your project files, or tell the linker to link with the GLee library (glee.lib or glee.dll for Windows, or libglee.a/libglee.so for Linux). OSX users will not need GLee, as OpenGL extensions are available by default. Note: The link on the website seems to be down, so you can download glee.h and glee.c from [https://www.dropbox.com/sh/de5yhhoq7nccxm9/wMVYsKwqRu/glee here].
+
 
+
We provide a [[Media:shader-class.zip|sample shader class]] and [[Media:shaders.zip|three sample combinations of vertex and fragment shaders]] for you to familiarize yourself with GLSL shader programming. For this exercise, extend the <tt>diffuse_shading</tt> shader to implement the desired effects.
+
 
+
If the respective light is off, the attraction/repulsion and color shifting effects need to stop.
+
 
+
Support a so far unused keyboard key to toggle the functionality of this part of the assignment on/off.
+
 
+
You will get points for the following things:
+
 
+
* A GLSL vertex shader with at least one attraction/repulsion effect ('''10 points''').
+
* A GLSL fragment shader with at least one color shifting effect ('''10 points''').
+
* Correct effect of the directional light ('''5 points''').
+
* Correct effect of the point light ('''5 points''').
+
* Correct effect of the spot light. The spot needs to be narrow enough to only affect a small part of the surface of the models ('''5 points''').
+
* Each of the 6 3D models needs to work with the shaders and you will need to be able to switch between the models with the keyboard. The models need to be big enough to roughly fill the OpenGL window. The light sources need to rotate like in exercise 2. ('''5 points''')
+
  
 
'''Notes'''
 
'''Notes'''
 
* While most CSE lab computers do, some older computers or simpler graphics cards do not support GLSL. Be aware that this might be a problem with your personal computer.
 
* While most CSE lab computers do, some older computers or simpler graphics cards do not support GLSL. Be aware that this might be a problem with your personal computer.
* Here are a few URLs with tutorials on how to write GLSL shader code.
+
* We provide a [[Media:shader-class.zip|sample shader class]] and [[Media:shaders.zip|three sample combinations of vertex and fragment shaders]] for you to familiarize yourself with GLSL shader programming more, if you find additional examples to the code on Lighthouse3D to be useful.
** [http://www.lighthouse3d.com/opengl/glsl/ Lighthouse3D GLSL tutorials]
+
* The Lighthouse3D example assumes that your spot light is your first light (light #0), so if your spot light is actually light #1 you need to change that in the shader.
** [http://www.clockworkcoders.com/oglsl/tutorials.html Clockworkcoders GLSL tutorials]
+
** [http://www.khronos.org/files/opengl44-quick-reference-card.pdf GLSL quick reference]
+
** [http://www.opengl.org/documentation/glsl/ GLSL reference documentation]
+
  
==4. Extra Credit (10 Points)==
+
==5. Extra Credit (10 Points)==
  
Create a simple water reflection effect for your 3D models, similar to that in the picture below:
+
Control the spot light with the mouse. To keep model rotations functional, the 'l' key should switch to light control mode, the 'm' key should switch back to the default model control mode.
  
[[Image:water-reflection.jpg]]
+
Control the spot light's opening angle with the mouse by dragging it up or down with the right mouse button depressed. ('''3 points''')
  
This effect does not need to be demonstrated concurrently with the shader effect from part 3, but it would be great if you could switch to it with a keyboard key.
+
When the user left-clicks with the mouse on a location on the surface of the 3D model, the spot light's direction should change to point directly at the point that was clicked on. ('''7 points''')
  
You will need to split the OpenGL window in two halves: the upper half shows the 3D model as you normally do, the lower half shows the same model, mirrored along the y axis and with a water-like reflection effect. You can constrain rendering for each of the halves of the screen by calling glViewPort before rendering each of the images, with different y coordinates and half the usual height.
+
Note that in order to find the position on the model the user clicked on, a ray-triangle intersection algorithm is needed: at the mouse click position, a ray should be cast parallel to the z axis into the model. Then every triangle on the surface of the object needs to be tested for intersection with this ray. If there are multiple intersection points, the one closer to the camera should be used. [http://www.lighthouse3d.com/tutorials/maths/ray-triangle-intersection/ This article on Lighthouse3D] describes such an algorithm in great detail.
 
+
The approach for the effect is to write a vertex shader, which (pseudo-)randomly offsets each vertex in an oscillating way using sin curves, shifting the vertices around the screen plane. Alternatively, you can use a different approach if you prefer, even frame-buffer based approaches which are vertex agnostic are acceptable.
+
 
+
You need to be able to rotate the 3D model as usual, and the water effect must constantly update the screen with slightly different offsets so that it looks like the water's ripples cause the effect.
+
 
+
GLSL does not have a native random function, but you can use the following popular pseudo-random number generator, which generates numbers based on two input values. The same input values yield the same random numbers, so you should seed the function with something like the current time, which you pass into the shader via a uniform variable, which your GLUT program updates with every frame.
+
 
+
<pre>
+
float rand(vec2 co)
+
{
+
  return fract(sin(dot(co.xy,vec2(12.9898,78.233))) * 43758.5453);
+
}
+
</pre>
+

Latest revision as of 12:11, 21 November 2014

Contents

Project 5: Light and Shade

This project is on OpenGL lighting and shading, and includes GLSL shader programs.

This project is due on Friday, November 21st at 3:30pm and will be discussed in CSB 001 on Monday, November 17th at 5pm.

Notes:

  • GLSL shaders will be covered in class on Tuesday, November 18th.
  • This project does not require a scene graph data structure. But we encourage you to use your scene graph for this and the remaining projects because it can make your code easier to write and debug.

1. 3D Model Loader (20 Points)

OBJ files are 3D model files which store the shape of an object with a number of vertices, associated vertex normals, and the required connectivity information to form triangles. Wikipedia has an excellent page on the OBJ file format. The file format is an ASCII text format, which means that the files can be viewed and edited with a text editor.

Here are three files for you to work with:

The files provided in this project only use the following three data types (other OBJ files may support more):

  • v: 'vertex': followed by six floating point numbers. The first three are for the vertex position (x,y,z coordinate), the next three are for the vertex color (red, green, blue) ranging from 0 to 1. Example:
    v 0.145852 0.104651 0.517576 0.2 0.8 0.4
  • vn: 'vertex normal': three floating point numbers, separated by white space. The numbers are for a vector which is used as the normal for a triangle. Example:
    vn -0.380694 3.839313 4.956321
  • f: 'face': lists three sets of indices to vertices and normals, which are the three corners of a triangle. Example:
    f 31514//31514 31465//31465 31464//31464

Lines starting with a '#' sign are comments and can safely be ignored.

Write your parser so that it goes through the OBJ file, line by line. It should read in the first character of each line and based on it decide how to proceed. Lines with parameters can be read with the fscanf command. Here is an example for vertices:

FILE* fp;     // file pointer
float x,y,z;  // vertex coordinates
float r,g,b;  // vertex color
int c1,c2;    // characters read from file

fp = fopen("bunny.obj","rb");
if (fp==NULL) { cerr << "error loading file" << endl; exit(-1); }

c1 = fgetc(fp);
c2 = fgetc(fp);
if (c1=='v') && (c2==' ')
  fscanf(fp, "%f %f %f %f %f %f", &x, &y, &z, &r, &g, &b);
// parse other cases and loop over lines of file

fclose(fp);   // make sure you don't forget to close the file when done

Once you loaded your model file, apply your full-screen algorithm from project 2.2 to scale the data sets up or down to match the screen size, and render the model. Use your rotation and scale routines to spin the model around and zoom in.

Grading:

  • -5 if only one data set loads
  • -5 if normals aren't correct
  • -5 if triangle mesh is incorrect

2. Mouse Control (30 Points)

Now it is time to improve on those keyboard commands for rotations of your 3D model. Add a mouse control function, to allow rotating the model about the center of your OpenGL window, as well as scaling the model up and down. The left mouse button should be used for rotation, the right mouse button should zoom in and out when the mouse is moved up or down while it is pressed.

This video shows how the trackball-like rotation 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.

Trackball.jpg

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.

To access the mouse x and y coordinates, you will need to use GLUT's callback functions glutMouseFunc(), which gets called when you press a mouse button, and glutMotionFunc(), which gets called constantly while you hold the button down and move the mouse. Note that successive trackball rotations must build on previous ones; at no point should the model snap back to a previous or default position.

For step by step instructions, take a look at this tutorial. Note that the tutorial was written for Windows messages, instead of GLUT mouse functions. This means that you'll need to replace the "CSierpinskiSolidsView::OnLButtonDown" function with "glutMouseFunc", "CSierpinskiSolidsView::OnMouseMove" with "glutMotionFunc", etc. To help you understand the code 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
}

Grading:

  • -5 if rotation is not in world coordinates
  • -5 if object's size changes when rotating
  • -10 if there is no zoom
  • -5 if rotation is in opposite direction
  • -10 if rotation is not cumulative

3. Fixed Function Lighting (30 points)

Write classes to manage light and material properties (Light and Material). As a starting point, refer to the relevant sections in Chapter 5 of the OpenGL Programming Guide, as well as the OpenGL Lighting FAQ.

Associate different material properties with each of the 3D model files: make one more shiny, another one more diffuse. Use colors other than white. (15 points)

Create two light sources: a point light and a spot light. Give them initial colors, positions, directions and opening angles as applicable. The spot light should have a spot width narrow enough so that it only illuminates a small part of the surface of the models (10 points). Draw a glutSolidSphere in the location of the point light (2 points), and a glutSolidCone in the location of the spot light, the wide end pointing in the direction of the spot (3 points). Make sure the light source positions are chosen so that both are visible on the screen. It may help to scale down the models some so that there is a longer distance between lights and model.

The lights should be stationary in world space: when the 3D model is rotated, the lights should not rotate with it.

Notes:

  • To ascertain that the normals of your 3D models will survive zoom operations correctly, you should use the following OpenGL command: glEnable(GL_NORMALIZE).
  • By default, OpenGL uses a simplified model for the calculation of the highlights. For a more realistic model add this command to your main() function: glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE).

4. Add Per-Pixel Lighting (20 Points)

Up until now, the spot light's outline on the surface of the 3D models has been rather fuzzy. Add per pixel lighting for the spot light to your application. To accomplish this, you need to add a vertex and a fragment shader to your application and enable them. In this project, you are free to copy-paste the shader code into your application from a source of your choice, for instance Lighthouse3D. You do not need to understand the shader code, just make it work.

Toggle this per-pixel lighting mode on and off with the 'p' key. When it is enabled, you do not need to render the effect of the point light, because this would require modifying the shader.

For Windows and Linux users, in order to use the OpenGL extensions for shaders, you should download GLee and add the glee.h and glee.c files to your project files, or tell the linker to link with the GLee library (glee.lib or glee.dll for Windows, or libglee.a/libglee.so for Linux). OSX users will not need GLee, as the OpenGL extensions are available by default. In case the GLee server is down, you can find the files in this ZIP file.

Notes

  • While most CSE lab computers do, some older computers or simpler graphics cards do not support GLSL. Be aware that this might be a problem with your personal computer.
  • We provide a sample shader class and three sample combinations of vertex and fragment shaders for you to familiarize yourself with GLSL shader programming more, if you find additional examples to the code on Lighthouse3D to be useful.
  • The Lighthouse3D example assumes that your spot light is your first light (light #0), so if your spot light is actually light #1 you need to change that in the shader.

5. Extra Credit (10 Points)

Control the spot light with the mouse. To keep model rotations functional, the 'l' key should switch to light control mode, the 'm' key should switch back to the default model control mode.

Control the spot light's opening angle with the mouse by dragging it up or down with the right mouse button depressed. (3 points)

When the user left-clicks with the mouse on a location on the surface of the 3D model, the spot light's direction should change to point directly at the point that was clicked on. (7 points)

Note that in order to find the position on the model the user clicked on, a ray-triangle intersection algorithm is needed: at the mouse click position, a ray should be cast parallel to the z axis into the model. Then every triangle on the surface of the object needs to be tested for intersection with this ray. If there are multiple intersection points, the one closer to the camera should be used. This article on Lighthouse3D describes such an algorithm in great detail.