Saturday, 9 February 2013

Assignment 5: Specular Lighting and Material Sorting

Requirements
  • Add ambient light to your scene
    • Ambient light should be specified entirely by a single color. That color should be added to the diffuse light (i.e. before the diffuse light is combined with the diffuse color), so that there is always some diffuse light.
  • Add falloff/attentuation to your pointlight
    • The specified intensity in your scene file should now refer to the intensity at its position, and that intensity should now decrease as the distance between the point light and the fragment increases
    • Any values you use to calculate the attenuation should come from the scene file
  • Create a new Effect that calculates specular lighting in addition to diffuse lighting
    • It should be able to use the same vertex shader that you already have created, but you will need to write a new fragment shader
    • You will need to add an expected uniform parameter to the Effect, so that different specular exponents can be set by different Materials
      • This particular uniform will be a float, but update your file format so that you could also handle a uniform float2 parameters, uniform float3 parameters, and uniform float4 parameters
  • Add a floor plane to your scene
    • This should be made of two triangles that form a plane in the XZ axis
    • Its height in Y should be fairly close to the bottom of the boxes
      • If you have followed the previous assignments' instructions, putting the plane at -1 in Y should work
      • Alternatively, you could put it at 0 in Y and then raise the boxes' positions
    • You may use either the Diffuse or the Diffuse+Specular Effect
  • Add enough boxes to satisfy the following requirements
    • You must use both of your Effects
      • In other words, some boxes must use only diffuse lighting, and some must use diffuse and specular lighting
    • For each effect at least two different materials must be used
      • Each material should use a different diffuse texture
      • The materials with specular lighting should use different specular exponents
    • For each material, at least two boxes must be drawn with that material
    • To be clear, this means you must draw at least 8 boxes
    • (BONUS) This isn't required, but if you're interested it would be a good exercise to also create a new Mesh file, so that you have a box with square normals and one with round normals, and to use these different Meshes for the two Entity boxes that use the same material
  • Sort your draw calls so that the following is true:
    • Every draw call using the same Effect is grouped together
      • Specifically, all four boxes using only Diffuse should be drawn in a row, and all four boxes using Diffuse and Specular should be drawn in a row. (It doesn't matter which Effect is drawn first or second.)
    • Within an Effect, every draw call using the same Material is grouped together
      • Specifically, the two boxes using the same Material should be drawn in a row. (It doesn't matter which Material is drawn first or second.)
  • You should minimize changes between draw calls so that the following is true:
    • You should only set new shaders when different Effects are drawn.
      • This means that you should only set shaders twice each frame. (You will have to set two different fragment shaders. It is perfectly fine to also set the vertex shader twice, although you can only set it once if you want to be clever and add a way to keep track of whether it's the same or not. I would advise for this assignment to not worry about the individual shader programs, and to always set every shader when a new Effect must be drawn.)
    • You should only set textures and uniform parameters when different Materials are drawn.
      • This means that when you draw two boxes that use the same Material you should only set the textures and uniform parameters before you draw anything. You shouldn't set any new state between the two calls to draw the boxes except for the model-to-world transformations. (Can you figure out why those must be set for every draw call?)
  • You should add PIX events to your code to make the sorting and state change optimizations clear
    • You are free to add extra events to organize things even more (like I showed in class), but the following eventsmust exist so I can see that you did the assignment properly:
      • Every new Effect should have its own event
      • Every new Material should have its own event
      • Every Entity should have its own event
    • As an example, your PIX events may look something like this:
      • Effect
        • Material
          • Entity
          • Entity
        • Material
          • Entity
          • Entity
      • Effect
        • Material
          • Entity
          • Entity
        • Material
          • Entity
          • Entity
  • Your code should only add PIX events in a debug configuration. It must not add them in release mode. I strongly advise #defining preprocessor macros to make this easier to manage. If you don't know how to do this, please ask the mailing list and I will be glad to help :)
  • Your writeup must include a screenshot of the PIX events
  • (BONUS) This isn't required, but you might consider adding an Entity that represents your point light so that it's easier to visualize how it's working. If you do this, you should make a new Effect with a fragment shader that doesn't get lit. Instead, the color of each fragment should just be the color of the point light.
    • (BONUS BONUS) If you used the attenuation function that takes a radius, can you figure out how to make the size of the box reflect this?
Details

Ambient Light
  • The color of ambient light should be quite dark. You probably shouldn't have any individual RGB value above 0.2
Point Light Attenuation
  • Light in real life obeys an inverse square law, meaning the intensity is proportional to 1/(d^2), where d is the distance from the light to the fragment.
  • In games a linear falloff is also often used, meaning the intensity is proportional to 1/d.
  • The obvious thing to do is to choose some constant k for your point light, and then use k/(d^2) or k/d, but this has some problems. The attenuation should only decrease the base intensity, and there is the risk of dividing by zero.
    • An equation that keeps the same simple spirit of the one above is to use either 1/(1 + k(d^2)) or 1/(1 + kd).
  • One interesting variation on the above you may want to try is 1/(1 + (d/r))^2, where r is the radius of the light (in other words, a spherical light with a bigger radius will be brighter than one with a small radius). Choosing a radius may be more intuitive for you than choosing an arbitrary constant "k" value. The equation above is from here, and I would encourage you to read that link to get a bit more insight into it and point light attenuation in general.
  • There are many variations off attenuation function. I would recommend choosing one of the ones I've listed here in bold, but feel free to try other things if you have time and interest. (If you have neither time nor interest, choose the recommended one here that seems the easiest :)
Specular LIghting
  • We will do classic Phong specularity in our class. The algorithm is something like:
    • Calculate the specular lighting
      • Get the normalized light direction L
      • Get the normal N
      • Calculate the reflected light direction R
        • The easiest way is with the HLSL instruction:
          • R = reflect(L, N)
        • You can also calculate this yourself:
          • R = L - (2 * N * dot(L, N) )
        • Note that L in this case is the vector from the light to the surface; if you have the other direction calculated remember that you can just use -L in either of the above equations
      • Calculate the normalized view direction V
        • This is the vector from the fragment to the camera
          • V = normalize(-g_transform_worldToView[3].xyz - position_world)
          • (Can you figure out why that's the position of the camera?)
      • Calculate how parallel the reflected light is to the viewer
        • dotProduct = saturate( dot(R, V) )
      • Raise that to the specular exponent to change the highlight based on how rough/smooth the material is
        • specularLighting = pow( dotProduct, g_specular_exponent )
      • Just like diffuse light, there shouldn't be any specular light when the surface normal is pointing away from the light
        • specularLighting *= dot(L, N)
        • (Or dot(-L, N), depending on what L is. Remember what you're doing conceptually, rather than just copying and pasting this code!)
      • Specular lighting should be affected by the light's intensity, but not by its color
        • specularLighting *= lightIntensity
    • Add the specular lighting to the fragment after the diffuse lighting has been combined with the diffuse color:
      • litFragment = ( diffuseColor * diffuseLighting ) + specularLighting
PIX Events
  • Use D3DPERF_BeginEvent() to start a new nested event
  • Use D3DPERF_EndEvent() to end a nested event
  • Each BeginEvent() call must be paired with an EndEvent() call
  • The documentation for these is hard to find... Microsoft seems to have erased it online, but it is still in the regular DirectX documentation (not in the specialized Graphics documentation like everything else we've used in class)
  • BeginEvent() asks for a color, which allegedly will color-code the event in PIX. I don't think this works, so you can just use a 0 for the D3DCOLOR.
  • BeginEvent() also wants a string for the label of the event, but this is a _wide_ string. The easiest way to do this is to preface string literals with a capital L, like so:
    • D3DPERF_BeginEvent( 0, L"My Cool Event" );

No comments:

Post a Comment