World Space MatCap Shading
This article was originally written in 2017 and doesn’t represent the idiomatic usage of MatCaps. Right now I would have probably used spherical harmonics for the same purpose. However, article may still be useful.
In our racing/runner game we decided to use matcap shading (at least for now)
MatCap (stands for Material Capture) does what it sounds like — captures a material properties like shininess / roughness, overall “feel” of the texture. It’s usually used just for that purpose — to create a feeling of some material at low rendering cost. MatCaps are usually associated with ZBrush and you can find a lot of MatCap examples on their site.
https://pixologic.com/zbrush/downloadcenter/library/#prettyPhoto
The cost of rendering is low because the whole material is just one texture sample away from rendering it on screen. All hard and expensive calculations are done “offline” while rendering the sphere itself.
As it’s been said, MatCaps are usually used for the material texture “feel”. But it’s also possible to use them just for lighting purposes only. We just need to capture our scene lighting on a non-reflective white sphere and then use that sphere to light any dynamic object on the scene. Much like in Spherical Harmonics, MatCaps rendering speed does not in any way depend on a number of light sources or anything else.
As we are making this lighting in Unity, a lot of work has already been done by other people. There’s a set of free MatCap shaders here: https://www.assetstore.unity3d.com/en/#!/content/8221
But there’s one problem — those shaders don’t use world-space normals for the MatCap sampling. The sampling depends soloely on viewport angle and position. It means that lighting depends on Camera rotation, not on Object rotation. Have a look:
Just by rotating camera we change lighting conditions. Lighting doesn’t really work like that. Basically this setup is OK for a static angle camera like some top-down RPG, or any other game where the camera doesn’t rotate.
But in our case there are camera rotations in some cases. So we need to be able to use world-space normals instead of the view-space normals. And this is how it looks like:
And if we rotate our camera around a sphere, we see this: Rotating around the sphere
The lighting remains the same — directions of whites and blacks remain the same.
While making changes to shaders I also noticed some artifacts along the “50% seam” where texture reads start to sample the very edge of the sphere:
I can think of two possible solutions:
- Texture Bleed along the edge of the sphere, so each pixel bleeds further into texture, reducing this white-brown edge to a zero
- Reduce the radius of MatCap texture sampling a bit
First solution is preferrable because it will allow to preserve these edge details. Second solution though is more simple and straightforward, so I decided to try that first:
As you can see we have lost that flare detail, but got rid of the artifacts.
And this is how the in-game car looks without and with MatCap lighting:
The shader can be found at https://github.com/KumoKairo/Worldspace-Normal-Lighting/blob/master/MatCap_Plain.shader