I’ve never liked how bilinear filtering makes normal maps resemble molten plastic when the camera gets too close. On certain types of surfaces like rock or rusty metal, this effect can be counteracted for some degree by applying a micro normal map. However when it comes to machine like assets with well defined, geometric features, that method doesn’t help much so I tried a different approach.
The idea is simple: let’s quantize the elevation of the normal (Z component) and have the filtering work for us by providing a less blocky contours.
Here are a few closeups of the test map, starting with a scenario where the input texture is rather low res:
256×256, DXT1
|
256×256, DXT1, Sharpened
|
The compression artifacts got exaggerated by the process, giving a somewhat crumpled look to the surface. It might even come in handy but if it’s not desired then uncompressed normal maps (V8U8) could be used:
256×256, V8U8
|
256×256, V8U8, Sharpened
|
(Please note that while the resolution is the same as above, this compression scheme produces a texture 4x the size of the DXT1.)
The overall look is better with much less crookedness on the edges but pixel thin details are a bigger problem than before.
Here are some higher resolution examples:
512×512, DXT1
|
512×512, DXT1, Sharpened
|
512×512, V8U8
|
512×512, V8U8, Sharpened
|
Since data is actually discarded during quantization I only blend in the sharpened version of the normal map when the viewer gets really close. The whole thing costs 16 instructions (13+3 for the depth based blending) and the a few parameters can be tweaked for optimal looks.
One could also create a mask texture to define where to apply the sharpening: very fine details and large, gradual elevation changes should be excluded.