ECE4893A/CS4803MPG: Multicore and GPU Programming for Video Games

Fall 2007

Homework #3: Now you are thinking with Shaders

Due: Tuesday, Oct. 30 at 23:59:59 (via T-square)

Late policy: The homework will be graded out of 100 points. We will accept late submissions; however, for every day that is it is overdue (including weekend days), we will subtract 20 points from the total. For instance, it you turn it in late but turn it in by Wednesday, Oct. 31 at 23:59:59, we will take off 20 points. (We understand that sometimes multiple assignments hit at once, or other life events intervene, and hence you have to make some tough choices. We'd rather let you turn something in late, with some points off, than have a "no late assignments accepted at all" policy, since the former encourages you to still do the assignment and learn something from it, while the latter just grinds down your soul.)

TA office hours for this assignment: Yimin will hold office hours in Klaus 2440 9:30-11:30 AM and 6-9 PM on Monday, and 2:30-5 on Wed.

Discussion board: We have set up a "HW #3" discussion board where students can discuss the homework and in particular ask questions that the professors, TA, and other students can help answer. (Try to avoid posting significant chunks of code on the discussion board; those are probably best directly e-mailed to the professors and the TA, or even better going to see the TA in office hours.

For this assignment, we will use the tutorial example framework associated with the book "The Cg Tutorial," by Randima Fernando and Mark J. Kilgard. (Note that you do not need to buy the book to do the assignment; pleanty of information about shader programming is just a few google searches away.) You can download the tutorial framework from the "Downloads" section here; I will also ask CoC and ECE support staff to install it on our NVIDIA and ATI equipped machines. The tutorial framework will let us explore Cg programming without needing to mess around with compiling DirectX code in Visual Studio. (There are two programs, NVIDIA's FX Composer and ATI's RenderMonkey, that allow you to explore shaders without writing a Direct3D or OpenGL host; these are immensely powerful programs, but also immensely complex. I was originally going to have you use them, but decided they would be too difficult to master in the time available. I wanted you to dig into Cg programming without having to learn such an intricate tool. You are encouraged to explore them on your own. They provide a bridge between the "artists" and the "programmers" working on a project.)

This homework will guide you through a series of tasks that explore various creative possibilities of shader programming. Each task will involve modifying code in the Cg Tutorial. The various examples may also be loaded using File->Open Setup (notFile->Open, which loads a particular shader program.) Some of the setups have sliders that let you tweak various parameters. If you click on "Edit Setup," you will see various specifications for the example, such as the textures that are loaded in, which parameters have sliders, etc. Note that Cg code associated with a particular "Setup" file will not typically work with Cg code designed for another "Setup" file, since each example typically involves different parameters. Each Setup has a default object that it loads, but you can use "Open Scene" to load in different ones.

My using the mouse in the upper right window, you can manipulate the camera (or sometimes light). Shift-click to translate the camera, and control-click to zoom in or out.

Very important: Before you modify any code, you must first save your own version of it somewhere under a different filename, or you may clobber the original demo code. This is particularly relevant on the shared lab machines.

1) Modify the C5_vertexLighting demo to employ a simple form of Gooch shading. Gooch shading attempts to mimic some techniques that artist use, particularly in technical illustrations, where the goal is to convey information, and not necessarily be "photorealistic." Select vertex color by linearly interpolating between blue (0,0,1) and yellow (1,1,0) according to the formula (1+L.N)/2, where L is the light direction, N is the surface normal, and the period indicates a dot product. Note this formula varies between 0 and 1; 0 should give blue, 1 should give yellow. Note that unlike with usual diffuse shading, surfaces will be visible (as blue) even if no light impacts it. (If you dig into Gooch's paper, you will see that what I have described and ask you to implement is the simplest and most jarring form of Gooch shading; the paper discusses combing this with the usual diffuse shading term and other interesting tricks. You need not do any of that here, but you may find it interesting reading).

Use "Open Scene" to load in an object that's not the car model, move the light and rotate the model from their default positions to some configuration you think looks cool, and take a screenshot.

2) Repeat the task in #1, except this time do it using the C5_framentLighting demo (so this is sort of Phong-style Gooch shading instead of Gourard-style Gooch sahding).

Use "Open Scene" to load in the same object you picked in problem 1, move the light and rotate the model from their default positions to some configuration you think looks cool (but that's different than what you used in 1), and take a screenshot.

3) Modify the C9_fog demo to implement a simple form of "depth cueing." Like Gooch shading, depth cueing is a nonphotorealistic technique that may help CAD designers when, for instance, viewing complex wireframe models (although here we're not using wireframe models). Change the pixel color calculation to multiply the texColor by the fogFactor, instead of linearly interpolating between the fogColor and the texColor. Also change the computation of the fogFactor to follow this linear "depth cue" formula (instead of the "fog" exponential formula):

fogFactor = max(0,min(1, (e - fogDistance)/(e-s))),

where e and s representing the starting and ending coordinate of the depth cueing effect (i.e. anything beyond e is not seen, anything closer than s looks the same). Also change fogDistance to be the z coordinate of eyeposition instead of the length of the eyeposition vector. Normally e and s would be passed in from the main application through variables, but to avoid having to edit the Setup file to add these variables, you may set e and s in your code. Experiment with values of e and s for your particular scene to get the a good illustration of the depth cueing effect.

Use "Open Scene" to load in an object that's not the default cityscape model. Experiment with values of e and s for your particular scene to get the a good illustration of the depth cueing effect, and take a screenshot.

4) Load the C8_bump demo. Move the light around (you can select to have the mouse move the light instead of the scene from the via the Control) menu and marvel at the drama of the bump mapping effect. Make a modified version of the bump demo with the following tweaks:

A) The fragment shader uses a "normalization cube map" because some older GPUs didn't include a normalization instruction (or even floating point) in their fragment shaders. Since we're using modern GPUs with a normalization command, change the code to use "normalize" instead of the normalization cube map. (Note: This demo is set to compile with a profile appropriate for older graphics cards. You will need to change the profile on the fragment shader via the Compile->Configuration menu option and upgrade it from fp20 to fp30 so your fragment shader will have access to the normalzie function.)

B) Let's make the scene moodier by incorporating a spotlight effect; see the last slide of Prof. Lee's "3D Rendering Pipeline (II) slides." We will multiply the intensity from dot(normal,light) calculation already given in the code by this spotlight term. We need to be careful not to be mislead by the variable names; lightDirection indicates the direction from the light to an arbitrary point on the surface. For a spotlight, we need to think of it as pointed in a particular direction; we'll assume the spotlight is always pointed at (0,0,0) in object space (which is the coordinate system this particular example takes place in). Hence, we'll need to pass in the raw lightPosition to the fragment shader. You can do this by outputting from the vertex shader via TEXCOORD2, and hence inputting it to the fragment shader via TEXCOORD2. (Remember that in the spotlight formulas Prof. Lee gives, the vectors must be normalized, so you'll need to do another call to normalize). Choose a power sufficiently high that the spotlight effect is quite dramatic.

Take a screenshot. (You will need to use the default flat wall; doing bump mapping on an arbitrary surface is more complex, and not something we really covered in class.)

5) Let's now play with the C6_particle demo. This is quite different than others; here, instead of rendering triangles, the API tells the GPU to plot point at the position indicated by the fragment output register POSITION that are squares of size indicated by fragment register PSIZE. This isn't something that showed up in Prof. Lee's Direct3D slides; it's just yet another complicated API call (OpenGL could do it too). Play with the various parameters in the demo a bit to get a feel for how it works. Be sure to move the camera around to see the particle fountain from different angels. It appears that the input variable "vInitial" is set to random values, with the range of those random values set by the "Start velocity" sliders, whereas the "Acceleration" sliders set a constant uniform "acceleration" variable. The tutorial code generates new particles and terminates them after a certain amount of time, and increments the time variable for each particle.

In this problem, we'll take advantage of the Acceleration sliders to represent things that aren't acceleration. We won't bother to change the names (that way we can avoid changing the ini file), but be sure to realize we're using them for another purpose.

Let's change the calculation of the particle position to this:

float4 pFinal = pInitial + 
           float4(0.1*acceleration.x*t*sin(acceleration.y*t+vInitial.x),
                  0.1*acceleration.x*t*cos(acceleration.y*t+vInitial.x),
                  t*4,0);

How did I come up with this formula? I wanted to simulate a Swirling Cone of Doom, something kind of like an orderly tornado. This could be a spell cast from a magic wand, or it could eminate from an alien spacecraft. Note that the z-position of the particle just marches foward linearly in time. The other two coordinates spiral outward in a circle. The acceleration.x variable has been hijacked to control it radial velocity, and the acceleration.y variable has been hijacked to control the speed of the spin. (Note that the acceleration sliders only do negative numbers; this isn't a problem, just realize that to the left is faster and to the right is slower.) I experimentally set the constants 0.1 and 4 so we'd get reasonable control ranges without having to change the inf file. I'm using vInitial.x to generate a random starting phase for the spin; by setting Start Velocity X to its highest value, we get an approximately uniform random starting phase.

Let's also change the pointSize to be a constant 2, and change the color scheme to be appropriate for a Swirling Cone of Doom. Let's have it fade from some color to white by setting two of the color components to c*t (it will automatically saturate to 1 when rendering the color) and setting the other component to 1. This will crossfade from some color to white. Pick either red, green, or blue to be the color component you set to 1, depending on which color you think indicates more doom. You will need to set c experimentally; tweak it until you like the effect.

Play with the "Acceleration X" and "Acceleration Y" sliders, and the camera position until you get something you think looks really cool. Deselect Animate Time under the main menu to freeze the animation, and take a screenshot of your Swirling Cone of Doom.

6) Let's pull up the simple C3_texture demo; we'll modify this to wave in a strange mirage-like fashion. A more random version of what we will do here is often used in games to simulate atmospheric diffraction in the presence of heat, for instance around jet engines or a dragon's breath. To add a time element, we will need to teak the Setup (i.e., the .ini) file a little bit. You will need to change the SceneType definition to be "default animateTimeAtStart," and you will need to add the line "float time:Time"; below the string definitions. You'll also need to add "uniform float time" to the fragment shader parameter list.

Instead of just using texCoord in the texture lookup in the fragment shader, you should first add a time-varying function of the form A*sin(B*texCoord.y)*sin(C*time) to the the xcoordinate of texCoord and D*sin(E*texCoord.x)*sin(F*time) to the ycoordinate of texCoord. (To get access to the "sin" function, you'll need to use the "Compile->Configuration..." menu item, while having the fragment shader tab selected, to change the fragment profile from fp20 to fp30.) Notice the deformation in x is determined by the y coordinate, and the deformation in y is determined by the z coodinate. A and D control the amount of deformation, B and E sort of set the distance between "deformation valleys", C and F control the speed. Pick A to be different than D, B to be different than E, and C to be different than F. Useful ranges for A and C are roughly on the order of 0.005 to 0.03, B and E are from 10 to 200, and C and F are 5 to 30.

When you run it, you should see the picture morph in strange oscillatory Ways. Experiment with various values of the parameter. You can also try using texCoord.x in the deformation formula for the x-coordinate and texCoord.y in the deformation formula for the y-coordinate. At an appropriate moment, deactivate "Animate Time" under the control menu to freeze the animation, and take a screenshot.

7) Load the C7_dispersion demo. Change the Frenel parameters so you have all refraction with no reflection. Use "Open Scene" to load in a model that's not the standard car; poke around in the model directory until you find something you like. Depending on the size of the model, you may need to use Ctrl-mouse to zoom the camera in or out. Change the Etas to be radically different values so you get a profound prism-like effect. Orient the camera to an interesting position, and take a screenshot.

8) Modify the C9_shadowMapping demo so that the shadow shows up as pure red (1,0,0) instead of black. You may see more red than you expect at first glance, but when you think about it it will make sense. Play with the camera and light positions, and then take a screenshot.

9) Load the C9_projTexturing demo. Play with the camera and light positions; in particular, zoom the light (use Ctrl-mouse) so that you see many copies of the texture. You should observe strange effects when many small versions of the texture appear packed in one space. Take a screenshot.

Deliverables: Package all your Cg code and your screenshots up as a StuffIt file, or gzipped tar file. Include "HW3" and as much as possible of your full name in the filename, e.g., HW3_Aaron_Lanterman.zip. (The upload procedure should be reasonably self explanatory once you log in to T-square.) Name your screenshots and your code according to the problem number, and include some variation of your name (firstname, lastname, or a bit of each) in the filename as well.. This is to avoid confusion if we happen to have multiple screenshots named, for instance, "problem1" up at the same time. Be sure to finish sufficiently in advance of the deadline that you will be able to work around any troubles T-square gives you to successfully submit before the deadline. If you have trouble getting T-square to work, please e-mail your compressed file to lanterma@ece.gatech.edu, with "MPG HW #3" and your full name in the header line; please only use this e-mail submission as a last resort if T-square isn't working.

When you submit your homework, please tell us an approximate number of hours you spent working on it, as well your thoughts on the homework, particularly suggestions for improving it in the future. (If you don't have any suggestions, that's OK, just tell us an approximate number of hours.) This will help us with future offerings of the class.

Ground rules: You are welcome to discuss high-level implementation issues with your fellow students, but you should avoid actually looking at one another student's code as whole, and under no circumstances should you be copying any portion of another student's code. However, asking another student to focus on a few lines of your code discuss why a you are getting a particular kind of error is reasonable. Basically, these "ground rules" are intended to prevent a student from "freeloading" off another student, even accidentally, since they won't get the full yummy nutritional educational goodness out of the assignment if they do.