Our goal for this assignment was to add diffuse lighting to our game.
Diffuse lighting is a model of lighting in which we assume that all light incident on an object is reflected uniformly in all directions. In other words, an objects face will not be brighter ( or darker) if we view it from certain directions. As long as we can view it ( i.e, we are no standing behind it), it will appear the same. This model of lighting is much easier to simulate than specular lighting, where the angle at which the light is incident on the object determines where it is reflected.
How do we determine how well lit an object is? Surface normals! Each vertex has a normal associated with it , and it lets us know which direction is “outward” for the vertex.
If we have knowledge about the direction of our light as well, it is fairly easy to calculate the component of that light which will interact with our object ,using the dot product ( dot product of 2 vectors A and B is given by |A||B|cos(theta), where theta is the angle between them).
Hence, if our light source is anti-parallel to our normal, the dot product value (dot(normal, -light_direction)) will be 1, indicating that the all of the light will interact with our object.
If they were perpendicular, the dot product would give 0, indicating that the light would not interact with our object. We clamp negative values to 0 ( since negative lighting wouldn’t make sense).
How do we translate these values to the color of our object’s fragments? Uptil now, the color of each fragment was determined by the sampled texture color, the color from the vertices, and the color modifier constant. Now, we will add another factor into this calculation. We calculate the dot product of the negative of the lights direction with the fragments normal, multiply this by the lights intensity, and factor it into the fragments color. This means that the fragment will be brightest when it is facing the light, and will gradually grow duller as it moves away from it. The intensity of the light will also determine the maximum brightness the color can attain. We also take into account some ambient light ( stray light arriving at the object after multiple reflections from surrounding object. These calculations would be too intensive to do accurately, and we just approximate ambient light). Even if an object is not receiving any light, the ambient light ensures that it is faintly visible.
Onto the actual implementation. We were already importing our meshes from maya, and had the normal data available to use. I changed my mesh format to take into account the normal data, and also had to change my mesh builder to read in the normals. I also had to change my vertex data to take into account the additional three floats for normals. The beauty of loading the mesh data as a binary file during run time is that we don’t have to change the way we load it, as it is already in the form we desire.
I added a light source class to my game, which has an intensity and a direction. This would store data for the lights I used in my game. I needed to pass normal data to the fragment shader, via the vertex shader, so that it could compute the lighting. I changed the vertex shader parameters so that they now accepted normals as well.
I transformed the normals from model space to world space in the vertex shader, and pass it into the fragment shader. I had to add additional constants to the fragment shader to account for light color and direction.
The fragment shader would get the interpolated normals from the vertex shader, which would need to be normalized, since they were no longer unit vectors. It would then calculate the dot product and find the component of the light that would interact with the object.
This would give us the right color for the fragment, taking into account lighting!
Here’s what my fragmentShader debug code looks like in PIX. It tells you how a particular fragment on my red cube got the color it is rendering.
As you can see, the diffuse amount ( the output of the dot product) is quite low ( 0.282), indicating that the light is more perpendicular to the cube face, than anti-parallel.
I also added a scene file for this assignment, which would contain information about all the actors in my game. Here’s what it looks like
I read this file in my game code to populate the world with actors.
This lets you create actors in the game by simply changing a human-readable file. You can decide its initial position, mesh and material. ( I will be adding more functionality as and when I need it, like velocity, and collision system status). Adding meshes, materials and actors to my game is now easier than ever! Want a new mesh to be added to an actor? Simply add it’s name to the meshList.txt, change the scene file to say that the actor uses a mesh with that name, and you’re all set! ( You’ll have to add the mesh to the assets folder, and add its name to the AssetsToBuild lua file. Maybe I should have my AssetsToBuild file generate the mesh names from my meshList.lua file? )
I also added a lua file to hold lighting information. It is very basic right now, and contains information regarding the ambient light color, and also the color of the single diffuse light in our game.
Another way to make life easier for people who want to tweak values inside my game, but don’t really want to look at my code 🙂
Right now, my diffuse light is located above my objects, pointing straight down. You can use the left control and left shift keys to move it along the x axis ( left control = -ve x axis, left shift = +ve x axis), and right control and right shift keys to move it along the z axis ( right control = -ve z axis, right shift = +ve z axis). The light will adjust itself in such a way that it will always point towards the origin.
That’s it for this assignment. Here’s a zip file containing my game: Click Here