I have torn through another chapter of GLSL 4.0 Shading Language Cookbook and had a blast doing it. The third chapter guides you through creating six shaders building on other shader techniques introduced in the previous chapter. If you don’t already own GLSL Shader Cookbook, I highly recommend it, but make sure to pick up the physical copy. I am using the Kindle version which creates a lot of issues with formatting of code that can drive a person mad. If you are already mad like me though, go ahead and pick up the digital copy so you can start right away!
So, our first shader introduces directional lights. Chapter two left us with only point lights. Point lights are nice, but directional lights are a necessity in game engines because they can simulate a light source that is very far away such as the sun. You can see below I have a marker for the directional light. It is hard to make out the direction of it due to me using an unlit shader on it, but you can kinda see where it is pointing. It is pointing down and into the scene away from the camera. I also have a blue light just barely out of view that is casting a little bit of extra color onto the character. You can also notice on the tail that the light for this is calculated on the vertex and interpolated in the fragment shader. The next shader addresses this which corrects some of the visual anomalies vertex shading presents us with.
Much better! Everything looks much more smooth and believable! The meat of this shader is the same as the previous shaders written that use the Phong ADS shader model. The big difference here is that it is happening for every fragment on the screen (or pixel if you want) instead of every vertex. Vertex shading is a good option if you are working on resource constrained hardware, but nowadays, even the cheapest of computers can render most objects with a per-fragment shader without much of a slowdown.
Of course, performance is always a concern for any self respecting game developer. The next shader improves on the previous one by introducing the concept of a half-vector for calculating the specular highlight in the scene. The half-vector is a technique commonly used to speed up a shader that uses specular highlights. In the previous shader, the specular color is calculated by using a reflection function in GLSL. The half-vector allows you to get a pretty convincing substitution of this value by summing the direction vector to the camera and direction vector to the light and normalizing it. When comparing the two calculations, the shader has to do less work with the half-vector and since this is running for every fragment in the shader, you save a good amount of performance without much of a difference in quality. You can compare the specular highlight of the previous shader with this one and see there isn’t any noticeable difference. Even if you do notice the difference, you can adjust the power of the material’s Shininess value to compensate for it.
Earlier, we introduced the directional light. Now we are implementing the concept of a spotlight. This doesn’t need a ton of explanation, but ultimately I created a new light type in my LightType enum, added some extra properties to lights to control the falloff and angle of the spotlight, and tied them all together. In this example, I have created two lights that cast light onto the squirrel and floor below him.
Next we have a shader that is more appropriate for the cartoonish squirrel model I have been using. This shader creates a cellshader effect by quantizing the dot product to a discreet collection of lighting levels, or in layman’s terms, it makes the lines between shadings of color much sharper to simulate a hand-drawn effect. This particular shader uses a hard-coded value to define the number of shading levels per light.
The final shader of this chapter adds fog into the mix. The bulk of this shader is still the Phong ADS model, but after calculating the color, the shader calculates the intensity of the fog based on the distance of the fragment from the camera. In this shader we just use the Z distance to figure that out, but if you want a more realistic value, you can use the distance function but that takes more processing power to do. Also, adding a power multiplier to the distance value can make it appear more natural. My example had three squirrels in a giant cube to show off the depth effect. Ignore the dark floors because this is the inside of a cube where the normals are pointing outward.
Chapter three was a fun chapter. After implementing the per-fragment Phong ADS shader, it got me re-energized for shaders and how awesome they can be. Sure, it is still a simple shader, but the quality boost of the scene was large. And, as always, make sure to check out the source code on GitHub to follow along. If you want to check out the specific version of the code from when I wrote this blog, you can pull down the branch with the ID of 29a1ef2d1bbdff09c17f0fa5726867362cb3f7ab. Let me know in the comments below how your experiences with this chapter treat you.