The Assignment

This assignment will build on your OpenGL code from assignment 4 (or mine, if yours was not working) by implementing normal mapping to add apparent detail without adding additional geometry. This method modifies the normals used for shading according to data stored in a texture.

Geometry Normal Map


You will need to do some conversions between coordinate spaces. These are the spaces in our application

  1. Character model: The origin is at the character's feet. The X and Z axes are horizontal and the Y axis is vertical. The X axis points in the direction the character is facing.
  2. World/Terrain space: The origin is in the center of the terrain. The X and Y axes are horizontal and the Z axis is vertical. The X axis points in the U texture direction and the Y axis in the V texture direction.
  3. View space: The eye is at the origin. The X axis points toward the right side of the view, Y points toward the top of the view, and Z points backwards. In shader code, you can use modelViewMatrix to convert points in model space (for the character) or world space (for the terrain) into view space, and modelViewInverse to convert points in view space back.
  4. Clip space: OpenGL clip space is used for the built-in vertex shader variable gl_Position. Also known as Normalized Device Space, vertices are transformed into this space for display. In this space, the view frustum is warped into an axis-aligned cube from (-1,-1,-1) to (1,1,1). You can convert points in view space to clip space with projectionMatrix or from clip space back to view space with projectionMatrixInverse.
  5. Screen space: OpenGL screen space is available in the fragment shader using the built-in variable gl_FragCoord. The Z component is depth, while the X and Y components are screen pixel location with 0,0 at the upper left corner. To convert from screen space back to clip space you would need to create a viewport transform from the window size.
  6. Tangent space: This is defined per point on the surface. It has the Z axis pointing in the direction of the normal, X pointing tangent to the surface in the direction of increasing U texture coordinate, and Y pointing tangent to the surface in the direction of increasing V texture coordinate.

To complete this assignment, you will need to construct the 3x3 TBN matrix to convert between view and tangent space. The columns of this matrix are the view-space Tangent, Bitangent (secondary tangent), and Normal. The third column of this matrix is the view-space normal, which you already have. The other two are computed per-vertex in the terrain constructor, but you will need to add two additional vertex arrays to pass them into the vertex shader, and two new interpolation parameters to pass them from the vertex shader to fragment shader (out variables in the vertex shader and in in the fragment shader).

normal map texture

Normal Map

Normal mapping is a technique that adds apparent small surface detail using a normal from a texture to modify the smoother normal from the geometry. It is common to define these textures in tangent space, where (0,0,1) is an unperturbed normal. This results in tangent-space normal maps being largely shades of blue when viewed as an image. The first (red) channel of the texture encodes perturbation of the normal in the U direction, and the second (green) channel encodes perturbation in the V direction.

You will need to add OpenGL code to load the pebbles-norm.ppm file and pass it into the fragment shader. In your fragment shader, unpack this texture from a color with range 0 to 1 per channel into a normal with range -1 to 1 per component. Build a TBN matrix, transform the normal-map normal from tangent to view space, and use it for shading.

Add code to toggle the normal map on and off by pressing the "N" key (see the "F" fog toggle for an example).

Extra credit

For up to 25 points of extra credit, implement the "Surface Gradient Based Bump Mapping Framework" technique as an alternative to the per-vertex tangent and bitangent. Toggle this approach on and off using the 'G' key. If you implement the extra credit, you should have both traditional TBN normal mapping with 'N' and surface gradient with 'G'.

Extra credit is only available if you submit by the original deadline or use your free late. If you submit late with the late penalty, you will not be eligible for any extra credit points.

You are also only eligible for extra credit if you tell us you did the extra credit in your assn5.txt and where to find the relevant changes in your code.


One of the primary strategies for shader debugging is outputing values to color and interpeting the results. Remember that values less than 0 will appear the same as 0, and values greater than 1 will appear the same as 1. Some useful strategies for remapping colors into visible ranges for debugging

  1. Rescale x ∈ [-1,1] to x' ∈ [0,1] using x' = x * 0.5 + 0.5
  2. Rescale x ∈ [a,b] to x' ∈ [0,1] using x' = (x - a)/(b - a)
  3. Rescale x ∈ [0,∞] to x' ∈ [0,1] using x' = x / (1 + x)
  4. Rescale x ∈ [-∞,∞] to x' ∈ [-1,1] using x' = x / (1 + abs(x))
  5. You can put different views of the same variable into red, green, and blue. I often like red for negative, green for positive, and blue for some mapping of the absolute value: vec3(x<0,x>0,abs(x)). This results in black if exactly 0, red to magenta for negative values, and green to cyan for positive values.

What to turn in

Turn in this assignment electronically by pushing your source code to your class git repository by 11:59 PM on the day of the deadline and tagging the commit assn5. Do your development in the GLapp directory, continuing to modify the code there. It is not necessary to make a copy. Version control allows us to check out the assignment 4 version for grading while you work on assignment 5, even if you commit and push along the way.

Also include an assn5.txt file telling us about your assignment. Do not forget to tell us what (if any) help you received from books, web sites or people other than the instructor and TA.

Check in along the way with useful checkin messages. We will be looking at your development process, so a complete and perfectly working assignment submitted in a single checkin one minute before the deadline will NOT get full credit. Individual checkins of complete files one at a time just before the deadline will also NOT get full credit. Do be sure to check in all of your source code, CMakeLists.txt, and updated .gitignore file, but no build files, log files, generated images, zip files, libraries, or other non-code content.

Be sure to include the details of the system you used for the assignment in your assn5.txt in case we have problems.