## Description

In this assignment, you will add an interactive preview of the scene, and implement Phong Shading in your ray tracer. For interactive display, you will use the OpenGL API that uses graphics hardware for fast rendering of 3D polygons. Note: with some configurations, software emulation might be used, resulting in slower rendering. You will be able to interactively pre-visualize your scene and change the viewpoint, then use your ray-tracer for higher-quality rendering. Most of the infrastructure is provided to you, and you will just need to add functions that send the appropriate triangle-rendering commands to the API to render orÂ *paint*Â each kind ofÂ `Object3D`Â primitive. In OpenGL, you display primitives by sending commands to the API. The API takes care of the perspective projection and the various other transformations, and alsoÂ *rasterizes*Â polygons, i.e., it draws the appropriate pixels for each polygon. (In lecture, we will talk about how this is done using theÂ *rendering pipeline*). In addition, the infrastructure we provide takes care of the user interface and how the mouse controls the camera.

To use OpenGL on Athena, you will first need to obtain access to the OpenGL libraries and header files. To do this, from an Athena prompt, type:

add mesa_v501

All files implementing OpenGL code should include the OpenGL header files:

// Included files for OpenGL Rendering #include <GL/gl.h> #include <GL/glu.h> #include <GL/glut.h>

We provide an updatedÂ `Makefile`Â to link with the OpenGL libraries on Athena Linux. If you are using Windows, then you may need to download the OpenGL libraries yourself fromÂ http://www.opengl.org.

### Tasks

- Add an OpenGL pre-visualization interface to your ray tracer, using theÂ
`GLCanvas`Â class. You will need to create aÂ`GLCanvas`Â instance in your main routine and call the following function:void glCanvas::initialize(SceneParser *_scene, void (*_renderFunction)(void));

TheÂ

`initialize`Â routine takes two parameters: The first is a pointer to the global scene. The second is the function that will perform the raytracing. TheÂ`GLCanvas`Â class is set up so that the renderFunction takes no parameters and has aÂ`void`Â return type. From within the real-time interface (with the mouse cursor within the frame of the GL display window), you can call the render function by pressingÂ`'r'`. Once the initialize routine is called, theÂ`GLCanvas`Â will take over control of the application and will monitor all mouse and keyboard events. This routine will not return, although the application can be terminated by pressingÂ`'q'`, by closing the window, or callingÂ`exit()`. - Modify your camera implementation to control the interactive camera. Copy-paste the code provided inÂ
`camera_additions.txt`Â into your camera files, and update it to re-normalize and re-orthogonalize the up, direction, and horizontal vectors (similar to the camera constructor). Make sure you read the comments provided inÂ`camera_additions.txt`, as these will help you avoid some common pitfalls. Also pay close attention to the variable names in the provided code, as these may not correspond to the variable names in your own code.Use the left mouse button to rotate the camera around the center of the scene, the middle mouse button to translate the scene center (truck), and the right mouse button to move the camera closer to or further from the scene (dolly). To prevent weird rotations of the camera, it is necessary to store the original up vector of the camera and define a new “screen up” vector that is the normalized orthogonal up vector for the current direction vector. You can test your implementation at this point. In theÂ

`GLCanvas::display()`Â function is a call to the providedÂ`drawAxes()`Â function, which will allow you to debug your camera implementation.Once you start working on yourÂ

`Object3D`Â paint methods (described below), you should comment out the call toÂ`drawAxes`. Once your paint methods are complete, verify that your camera manipulation code is correct by moving the camera, rendering the scene & then comparing the pre-visualization to the raytraced result. - Derive a newÂ
`PhongMaterial`Â class fromÂ`Material`Â that adds a specular component (highlight). The constructor expected by the scene parser is:PhongMaterial::PhongMaterial(const Vec3f &diffuseColor, const Vec3f &specularColor, float exponent);

Make the originalÂ

`Material`Â class pure virtual by adding a pure virtual methodÂ`Shade`Â that computes the local interaction of light and the material:virtual Vec3f Material::Shade (const Ray &ray, const Hit &hit, const Vec3f &dirToLight, const Vec3f &lightColor) const = 0;

It takes as input the viewing ray, the Hit data structure, and light information and returns the color for that pixel. Implement the the Blinn-Torrance version of the Phong model taught in lecture. We suggest this version simply because it is what OpenGL uses, and the pre-visualization will thus be more similar.

Note: We’ve only included one type of light source in our scenes, directional light sources, which have no falloff. That is, the distance to the light source has no impact on the intensity of light received at a particular point in space. So you may ignore the r

^{2}Â term in the Phong lighting model for this assignment. Next week we will add a second type of light source, a point light source, and the distance from the surface to the light will be important. - OpenGL can also compute local illumination. You’ll need to add theÂ
`virtual void Material::glSetMaterial() const`Â member function toÂ`Material`Â (we have provided the code). This function will send the appropriate OpenGL commands to specify the local shading model.In the examples below we illustrate an artifact that occurs at grazing angles for wide specular lobes (small exponents). To solve this problem, the specular component can be multiplied by the dot product of the normal and direction to the light instead of simply clamping it to zero when this dot product is negative. You may implement either method in your ray tracer. To enable the specular lobe fix in OpenGL, a 3-pass rendering has been provided in the code (by default only the single pass rendering is performed). It is not required that you use or understand this code.

- Add a pure virtualÂ
`void paint()`Â method toÂ`Object3D`Â and implement it for each subclass. This function executes the appropriate OpenGL calls to render each object in the pre-visualization interface. Before sending any OpenGL geometric commands, you will need to call theÂ`void Material::glSetMaterial() const`Â member function to set up the OpenGL material parameters. Some useful code is provided inÂ`object3d_additions.txt`.**Group –Â**Similar to its intersection method, a group implementsÂ`paint()`Â by iterating over all its children and calling theirÂ`paint()`Â methods.**Triangle –Â**OpenGL is based on polygons. You tell the API to render a polygon by first telling it that you start a polygon, then describing all the vertices and their properties, and finally closing the polygon. The code to specify just the positions of a single triangle looks like this:glBegin(GL_TRIANGLES); glVertex3f(x0, y0, z0); glVertex3f(x1, y1, z1); glVertex3f(x2, y2, z2); glEnd();

Alternatively, you can directly specify an array of floats for each vertex usingÂ

`glVertex3fv(float *array)`. To set the triangle normal, use one of the following commands before specifying the vertices:glNormal3f(float x, float y, float z); // List of floats glNormal3fv(float *arr); // Array of floats

Remember that you can compute the normal of a triangle using a cross product.

**Plane –Â**OpenGL does not have an infinite plane primitive. To pre-visualize planes, you will simply draw very big rectangles. Project the world origin (0,0,0) onto the plane, and compute two basis vectors for the plane that are orthogonal to the plane normalÂ**n**. The first basis vector may be obtained by taking the cross product betweenÂ**n**Â and another vectorÂ**v**. Any vectorÂ**v**Â will do the trick, as long as it is not parallel toÂ**n**. So you can always useÂ**v**=(1,0,0) except whenÂ**n**Â is along the x axis, in which case you can useÂ**v**=(0,1,0). Then the first basis vector,Â**b**, isÂ_{1}**v**Â xÂ**n**Â and the second basis vector,Â**b**, isÂ_{2}**n**Â xÂ**b**. Display a rectangle from (-_{1}*big*, –*big*) to (*big*,Â*big*) in this 2D basis, for some big numberÂ*big*. (Caution: OpenGL does not like rendering points at INFINITY.Â*big*Â should probably be < 10^{6})glBegin(GL_QUADS); glNormal3f(nx, ny, nz); glVertex3f(x0, y0, z0); glVertex3f(x1, y1, z1); glVertex3f(x2, y2, z2); glVertex3f(x3, y3, z3); glEnd();

**Sphere –Â**OpenGL does not have a sphere primitive, so spheres must be transformed into polygons, a process known asÂ*tessellation*. (Actually,Â`glu`Â does have a sphere primitive, but you are not allowed to use this shortcut for the assignment). You will implement the classic sphere tessellation using angular parametersÂ*theta*Â andÂ*phi*. The number of steps inÂ*theta*Â andÂ*phi*Â will be controlled by the command line argumentÂ`-tessellation <theta-steps> <phi-steps>`. Deduce the corresponding angle increments, and use two nested loops on the angles to generate the appropriate polygons. Note thatÂ*theta*Â should vary between 0 and 360 degrees, whileÂ*phi*Â must vary between -90 and 90 degrees.In OpenGL, you may include 3*n*Â vertex positions within theÂ`glBegin`Â andÂ`glEnd`Â commands to drawÂ*n*Â triangles (or 4*n*Â vertices to drawÂ*n*Â quads):glBegin(GL_QUADS); for (iPhi=...; iPhi<...; iPhi+=...) for (int iTheta=...; iTheta=...; iTheta+=...) { // compute appropriate coordinates & normals ... // send gl vertex commands glVertex3f(x0, y0, z0); glVertex3f(x1, y1, z1); glVertex3f(x2, y2, z2); glVertex3f(x3, y3, z3); } } glEnd();

You will implement two versions of sphere normals: flat shading (visible facets) andÂ

*Gouraud interpolation*. By default your previsualization should use flat shading; that is, simply the normal of each triangle, as you would for polygon rendering. When theÂ`-gouraud`Â option is specified, your pre-visualization should render with Gouraud interpolation, and use the true normal of the sphere for each vertex (set the vertex normal before specifying each vertex position). Note how this improves the appearance of the sphere and makes it smoother. OpenGL performs bilinear interpolation between the shaded color values computed at each vertex. Remember that this is not as good as theÂ*Phong interpolation*Â described in class (which interpolates the surface normals and then performs the lighting calculation per pixel).**Transformation –Â**Finally, you must handle transformations. OpenGL will do most of the work for you. You only need to specify that you want to change the current object-space-to-world-space 4×4 matrix. To do this, you first need to save the current matrix on a matrix stack usingÂ`glPushMatrix()`. Then change the matrix usingÂ`glMultMatrix(GLfloat *fd)`. Use theÂ`glGet()`Â routine to theÂ`Matrix`Â class to construct a matrix with the appropriate structure. OpenGL matrices created with this routine should be deleted when they are no longer needed:glPushMatrix(); GLfloat *glMatrix = matrix.glGet(); glMultMatrixf(glMatrix); delete[] glMatrix;

Then, recursively call theÂ

`paint()`Â method of the child object. After this, you must restore the previous matrix from the stack using:glPopMatrix();

If you do not save and restore the matrix, your transformation will be applied to all the following primitives!

### Hints

- As usual, debug your code as you write it. Initially implement emptyÂ
`paint()`Â functions for the Object3D subclasses. Run intermediate examples to make sure that sub-parts are sane. - To improve performance, you may extract the values that do not need to be recomputed for each frame and cache them. For example, you might want to store triangle normals or the tessellation coordinates of a sphere. As usual, there is a trade off between speed and memory.

### Ideas for Extra Credit

- Implement the original Phong model (in addition to the Blinn-Torrance variation) and compare, add an approximation of the fresnel effect, implement a fish eye camera, etc.

### Updated Files:

If you’re interested, here’s theÂ scene description file grammarÂ used in this assignment.

If you’re interested, here’s aÂ list of command line argumentsÂ used in this assignment.

### Input Files

- scene3_01_cube_orthographic.txt
- scene3_02_cube_perspective.txt
- scene3_03_bunny_mesh_200.txt
- scene3_04_bunny_mesh_1k.txt
- scene3_05_axes_cube.txt
- scene3_06_crazy_transforms.txt
- scene3_07_plane.txt
- scene3_08_sphere.txt
- scene3_09_exponent_variations.txt
- scene3_10_exponent_variations_back.txt
- scene3_11_weird_lighting_diffuse.txt
- scene3_12_weird_lighting_specular.txt

### Sample Results

raytracer -input scene3_01_cube_orthographic.txt -size 200 200 -output output3_01.tga -gui

Â

raytracer -input scene3_02_cube_perspective.txt -size 200 200 -output output3_02.tga -gui

Â

raytracer -input scene3_03_bunny_mesh_200.txt -size 200 200 -output output3_03.tga -gui

Â

raytracer -input scene3_04_bunny_mesh_1k.txt -size 200 200 -output output3_04.tga -gui

Â

raytracer -input scene3_05_axes_cube.txt -size 200 200 -output output3_05.tga -gui

Â

raytracer -input scene3_06_crazy_transforms.txt -size 200 200 -output output3_06.tga -gui

Â

raytracer -input scene3_07_plane.txt -size 200 200 -output output3_07.tga -gui -tessellation 10 5

Â

raytracer -input scene3_08_sphere.txt -size 200 200 -output output3_08.tga -gui -tessellation 10 5 raytracer -input scene3_08_sphere.txt -size 200 200 -output output3_08.tga -gui -tessellation 20 10 raytracer -input scene3_08_sphere.txt -size 200 200 -output output3_08.tga -gui -tessellation 10 5 -gouraud raytracer -input scene3_08_sphere.txt -size 200 200 -output output3_08.tga -gui -tessellation 20 10 -gouraud

Â

Â Â

raytracer -input scene3_09_exponent_variations.txt -size 300 300 -output output3_09.tga -gui -tessellation 100 50 -gouraud

Â

*and with the OPTIONAL specular fix:*

Â

raytracer -input scene3_10_exponent_variations_back.txt -size 300 300 -output output3_10.tga -gui -tessellation 100 50 -gouraud

Â

*and with the OPTIONAL specular fix:*

Â

raytracer -input scene3_11_weird_lighting_diffuse.txt -size 200 200 -output output3_11.tga -gui -tessellation 100 50 -gouraud

Â

raytracer -input scene3_12_weird_lighting_specular.txt -size 200 200 -output output3_12.tga -gui -tessellation 100 50 -gouraud

Â

*and with the OPTIONAL specular fix:*

Â

See the mainÂ Assignments PageÂ for submission information.