Implementing Team Fortress 2 (TF2) Shader in C++
Introduction
As a nerdy gamer who wasted 2000+ hours of life in competitive Team Fortress 2 gaming, I always loved the unique cartoon art style of the game. I looked at the shading paper from the game and it seems mixed with many shading techniques. I wonder if I can replicate the implementation of it.
Paper link: Illustrative Rendering in Team Fortress 2
The formula of TF2 shading
The shading in Team Fortress 2 (TF2) has a blend of cartoon and realistic styles. According to the conference paper of TF2 shading, a variety of view independent lighting and view dependent lighting are combined in the TF2 model shading. To be exact, the complete shader in Team Fortress 2 is merely the summation of the view independent lighting term and view dependent lighting term. The view-independent lighting term can be formulated as:
where is the diffuse component, is the ambient light, is the normalized surface normal at point , is the normalized incoming light direction vector at point , is the light intensity at point from light source. is essentially the Half Lambert term, but in Team Fortress 2 shading a different parameter setting is used such that . is an artistic warping function to tune the Half Lambert term.
On the other hand, the view-dependent light term can be formulated as:
Looks familar right? Thats the favourite equation of TF2 engineer:
where is the light intensity at point from light source. is a specular mask painted into a texture channel. is an artist-tuned Fresnel term for general specular highlights, is another Fresnel term used to mask rim highlights. is the view direction. is the reflected vector i.e. . is the specular exponent fetched from a texture map, is a constant exponent which controls the breadth of the rim highlights. is the rim effect result from the dot of the normal vector and up vector. is the ambient light. From the phong terms, we can see the larger value of phong term will be selected, which means either the ‘spec’ phong term or ‘rim’ phong term will be selected.
Final result adding up two lights together.