Artem Krizhanovskiy demonstrated the Indexed Material Mapping approach that allowed him to increase detail without memory loss by sampling all material data from pre-defined LUT.
The main idea of this approach is to reduce compression artifacts, increase detail in all PBR channels, get highly customizable shading without any changes in the content, and in the end reduce the memory.
Instead of using direct texturing approach, I rely on a material-based approach in which using a gradient (wear or condition) and material we can sample all material data from pre-defined LUT.
The only input is:
- 4-channel full-sized gradient-ID map, compressed
- Full-sized Tangent or object-space normal map, compressed
- 16x16 albedo, repaint LUT, uncompressed
- 16x16 metallic, roughness LUT, uncompressed
Gradient Texture: R - Ambient Occlusion, G - material ID, B - detail ID, A - gradient
Material LUT1: RGB - albedo, A - repaint mask | uncompressed, no mip-map
Material LUT2: R - metallic, G - roughness, BA - empty | uncompressed, no mip-map
Basically, gradient serves as U and material ID map serves as V in reading Material LUT textures: texture(materialLUT_sampler, vec2(gradient, material_ID))
Schematically, it looks like this:
The process is pretty straightforward and can be reproduced with code or node-based editors like Substance Designer or Unreal Engine:
This model consists of 16 materials:
In this case, additional camo pattern texture is used as a repaint color.
The very idea of repaint is based on reversed blending. As we know, trivial linear interpolation looks like this:
Result = C_base*(1-alpha) + C_paint*alpha
We can derive C_base:
C_base = (Result - C_paint*alpha) /(1-alpha)
And blend it with a new color:
Result = C_base*(1-alpha) + C_repaint*alpha
The Devil Is in the Detail
The detailed (top) image has 16 times greater texel density while it is only ~11% heavier on memory.
The procedural nature of this approach allows me to apply a full-PBR set of detail to the model which works fine with all materials and repaint options. The detail map consists of 16 tiles with a set of detail normal and detail gradient. Detail_ID map is used to define which tile is used for detail. I would recommend using BC7 texture format with normal R and G channel in RG, and gradient in B. Further restoration of normal B channel is required in code. You may notice that some of the details are blank, that's because I don’t need 16 details on this model.
You can see how changing detail_ID affects the surface: