While I was working at Jackson Dawson, one of my major projects was to develop a car paint shader. Our projects were built primarily using unity with a C#, and so was my shader. Car paint traditionally is comprised of three layers; the clear coat layer, the base coat layer, and the metallic flake layer. The existing car paint that we were using, one of the top options from the Unity app store, had a lot of problems; the clear coat layer was causing the light to get blown out and from a distance the car would look completely white regardless of its paint color.
The base layer is pretty simple, just a standard PBR shader, I emulated Unity's instructions with this one. The real complexity started when I began introducing the clear coat shader. The clear coat layer is a glossy layer of transparent material that coats the outside of the car. Its used to protect the other layers of paint Right away this forced me to write the shader in Forward Rendering mode only, as the differences in normals from the base + flake layer and the clear coat layer is incompatible with deferred rendering in unity, and I couldn’t figure out any way around this other than to write the shader in the forward rendering mode. At some point I want to try tackling this problem again when the new scriptable rendering pipeline system comes out in Unity to create a deferred pipeline capable of handling multiple layered materials.
The challenge with the clear coat layer was figuring out how to blend the base and clear coat layers. This was where the previous shader fell down as it just added the two layers together, causing blowout when two bright spots overlapped. Scientifically any light reflected off the clear coat layer shouldn’t reach the lower layer, but trying to convert that into an algorithm that could run in a manner that didn't kill performance was decidedly more difficult. The awkward part of this problem was trying to determine how much light was going to get filtered out of the base came from calculating how much light was getting reflected away by the clear coat that wasn't ever reaching the camera, and how that how
I tried to figure out an analytical solution based off of science that wouldn't be too performance intensive, but I couldn't figure one out during the time that I had to work on this project. So I wound up experimenting a lot and just trying things out until I found a solution that worked fine.
Another thing I was working on while I was writing this shader was implementing shader features to optimize the shader so that it didn’t run anything that the artist didn’t want it to. As a result the car paint shader also works as a simpler Clear Coat shader that works perfectly for materials like carbon fiber. And it will simply ignore all the flake calculations.
The next major challenge came with the flake layer. In concept, the layer is a bunch of tiny flakes that reflect light in different directions, and cause a light sparkle when you look at car paint. Getting this to function in game is a lot more difficult. And probably deserves a writeup all to itself, so expect that coming sometime soon.