# Assignment 3 - GLSL Shaders

Due Wednesday 2/26 at 11:59 pm. You must work individually.

## Goal

Integrate the various concepts learned in the lectures and create a program that can color the mesh with various materials, lights, and shaders.

## Associated Labs

• Lab 0 (required): Setting Up Your Development Environment
• Lab 5 (optional): Introduction to OpenGL & GLSL
• Lab 6 (optional): Further Introduction to OpenGL & GLSL
• Lab 7 (required, recommended starting point): Phong Fragment Shading

Complete Lab 7. Make sure you can get the cube to render exactly as shown in the lab.

Then, create three materials and add keyboard hooks to cycle through them using the m/M keys. You should create a Material class for this. The three materials should roughly look like the following.

In these examples, the light is set at (1, 1, 1) in camera space. The first material should be blue with green highlights. The second material should be grayish with low shininess. The third material should be pinkish with very strong highlights. You don’t need to match the 1st and 2nd colors exactly to the images, but the 3rd one must be exactly the same as the Lab 7. (See the bottom of Lab 7 for the values.) As before, we’re not going to do attenuation in this assignment.

Add an extra light to the Phong shader. You should create a Light class for this. The final fragment color is going to be a simple sum over the lights. Since our light is going to have no color (R=G=B), we can use the following formula.

$c = \sum_{i=1}^n c_L (c_{Ai} + c_{Di} + c_{Si}),$

where $$c_L$$ is the scalar intensity of the ith light. Use these values for the light positions and intensities:

• Light 1
• Position: (1.0, 1.0, 1.0)
• Intensity: 0.8
• Light 2
• Position: (-1.0, 1.0, 1.0)
• Intensity: 0.2

Add keyboard hooks so that you can move the lights. Use the keys l/L (lower and upper L) to cycle through the lights, and the keys x/X and y/Y to move the selected light in the -X, +X, -Y, and +Y directions respectively. Test your program with cube.obj. (Add a commandline argument for the input obj file.) When you load the program, before you move the mouse, you should get exactly the figure shown here — the stronger light to right and a weaker light to the left. Make sure the materials are exactly the same as from the lab.

Create a silhouette shader. This shader should color all fragments white, except for those fragments whose normals form a right angle with the eye vector. Take the dot product of the normal and eye vectors and threshold the result. If the result is “close enough” to zero, then color that fragment black. Remember that both of these vectors need to be in the camera space. Use the s/S keys to cycle through the two shaders (Phong and Silhouette).

Make sure you create separate handles for the attribute and uniform parameters for each GLSL program you create. For example, the call

glGetAttribLocation(pid, "aPos")

should be made for each pid you create. In other words, you need to call addAttribute(...) in each Program instance you create even if the attribute name is the same for the two programs. Ditto for uniform.

You should not use an if/switch statement in the vertex/fragment shader to toggle between Phong and Silhouette shaders. Even though this works fine, it is not the right way to do things. First, it adds an extra conditional in the fragment shader, which is executed for every single vertex or fragment, which is bad from an efficiency point of view. Second, this may work for a simple assignment, but in a bigger project with multiple objects, this will get ugly quickly. The proper way is to put the if/switch inside the CPP file. You should do something like this:

if(something) {
glUseProgram(pid0);
} else {
glUseProgram(pid1);
}

Of course, there are other ways to do this as well. The important thing is to not have the if/switch statement in your shaders for toggling between Phong/Silhouette. We want the shaders to be as simple as efficient as possible, since they are run for each vertex and fragment.

Also, be sure to send in only the uniform variables that are needed for the currently bound program. If you set verbose to true, then you’ll see warnings if you try to send in uniform variables that do not exist in the currently bound program.

Note: The silhouette shader does not work well for the cube because it has sharp edges. Test your code on the bunny.

## Interaction summary

• m/M: cycle through materials
• l/L: cycle through lights
• x/X: move selected light in x
• y/Y: move selected light in y
• s/S: cycle through shaders

## Debugging OpenGL & GLSL

• Set the Program class to be verbose by calling the setVerbose() function. If there is a GLSL compilation error, then you will see the error in the console. For example, if the varying variables of the vertex shader and the fragment shaders do not match up, it will tell you so. Make sure to set verbose to be false after debugging.

• Use GLSL::checkError(GET_FILE_LINE); to find which OpenGL call caused an error. This function will assert if there were any OpenGL errors before getting to this line. You can use this to winnow down which OpenGL function is causing an error. For example, if you put this line at the top, the middle, and the bottom of your function, and if the assertion happens in the middle, you know that the error must be happening in the top half of your function. Once find exactly which OpenGL call is causing the error, you can Google the OpenGL function to figure out what caused the error. For example, maybe one of the arguments should not have been zero or null.

## Point breakdown

Your program will be tested using both the bunny and the cube.

• 10 points for multiple materials that can be cycled with the keyboard.
• 15 points for multiple lights that can be moved with the keyboard.
• 5 points for correct implementation of the ambient component.
• 10 points for correct implementation of the diffuse component.
• 15 points for correct implementation of the specular component.
• 30 points for the silhouette shader.
• 15 points for coding style and general execution (e.g., Material and Light classes).
• 5 points bonus for a cel shader.

Total: 105 points

## What to hand in

Failing to follow these points may decrease your “general execution” score. Make sure that your code compiles and runs by typing, for example:

> mkdir build
> cd build
> cmake ..
> make
> ./A3 ../resources ../resources/bunny.obj
• Make sure the arguments are exactly as specified.
• Include a README file that includes:
• Hand in src/, resources/, CMakeLists.txt, and your readme file. The resources folder should contain any input obj files.
• Do not hand in the build directory, the executable, old save files (*.~), or object files (*.o).
• Create a single zip file of all the required files. The filename of this zip file should be USERNAME.zip (e.g., sueda.zip). The zip file should extract everything into a folder named USERNAME/ (e.g. sueda/).
• When you unzip this file, it should extract src/, resources/, CMakeLists.txt, and your README file to the current directory.