Today I want to share with you a trick which my colleague from previous job mentioned to me a long time ago. It's about alpha tested (also know as cutout) materials. This technique which I want to share with you consists of two neat tricks that can improve the quality of alpha tested (cutout) materials.
Alpha test is an old technique used in computer graphics. The idea behind it is very simple. In a very basic form, a material (shader) of a rendered object can discard processed pixels based on the alpha channel of RGBA texture. When shaded pixel’s final alpha value is less than this threshold value (threshold value is constant for the instance of the material and a typical value is 50%), it is clipped (discarded) and will not land in the shaders output framebuffer. These types of materials are commonly used to render vegetation, fences, impostors/billboards, etc.
Figure 1: Source: https://opengameart.org/content/free-handpainted-plants-2
Alpha tested materials have some issues. It can be noticed when a rendered object (with applied cutout material) is far away from the camera. Let the following video below be an example of this issue.
In the standard way of generating alpha textures an object which is using this material will tend to degenerate (disappear) when moving far away from the camera. This problem occurs because of the way mipmaps are generated for those alpha tested textures. Mipmapping is a technique in computer graphics that was developed to help avoid aliasing artifacts (shown in Figure 2) and thus render a better quality image.
Figure 2: Source: https://en.wikipedia.org/wiki/Mipmap#/media/File:Mipmap_Aliasing_Comparison.png
To avoid artifacts shown in Figure 2. GPU needs to have two things:
1) material textures must have mipmaps. Mipmaps are downsized duplicates of the original image and form a chain of images. Each level is two times smaller than the previous level.
Figure 3: Source: https://en.wikipedia.org/wiki/Mipmap#/media/File:MipMap_Example_STS101.jpg
2) The material sampler filtering type must have proper value (in OpenGL
Generating a downsized version of the original image can be done by using algorithms called filters. In computer graphics a lot different filters have been developed (with different usages and characteristics, I won’t go into details for this text). I want to mention two of them bilinear filter and Kaiser filter.
The simplest filter that can create a mipmap will use an average of 4 neighbor pixels to generate one pixel for the next level. And this averaging process is a root cause of losing quality in alpha tested materials. I would like to describe it in more detail on the following example. In tables below only alpha channel values will be shown for 4 levels of mipmaps and mipmap levels are generated by using the averaging process.
1 level mipmap
2 level mipmap
3 level mipmap
4 level mipmap
Let's check how many pixels for each mipmap level passed the alpha test (they were not discarded).
|Mipmap level||Pixel passing alpha test|
As we can see, fewer and fewer pixels passed the alpha test on each mipmap level. This effect more visible if the original image contains smooth gradients in the alpha channel. This is why objects are degenerate when moving far away from the camera.
How we can improve quality and fix this issue? There are two ways.
First way, we can remap alpha channel values (by using the equation shown below) so that the lowest value is not 0% but 33% (for alpha test threshold of 50%). by using this simple trick, the biggest mipmap level pixel it has the same behavior in an alpha test (passing or failing it). But its mipmaps will have a slower rate of “degenerating”.
An equation which can be used for remapping of alpha channel values looks like this:
Anew = MAX(Aold; 1/3 * Aold + 2/3 * ClampV)
where ClampV is the alpha test threshold used in alpha test material.
How will the alpha value behave when applying this technique for previously tested mipmaps? Let’s write a new table with alpha values (after remapping) for each mip level using again the same averaging process:
1 level mipmap
2 level mipmap
3 level mipmap
4 level mipmap
After remapping, the percentage of pixels that pass the alpha test on tested mipmap levels can be seen in the table below:
|Mipmap level||Pixel passed alpha test|
As can be seen, the usage of remapping causes that the number of pixels on individual mipmaps level that pass the alpha test to not decrease (as it was in the case before remapping) but even increases.
What are the consequences of the proposed remapping of alpha channel values? An object seen from a distance does not disappear but in fact it looks like a solid (more opaque) object. When you think about it, this is what we experiencing in the real world. When you, for example, look closely at a tree or a fence, you can see holes in the fence or spaces between individual leaves, but from a distance, those objects look like one opaque object.
2. The second thing that can be done is to set “better” RGB channel values for pixels that are cutout (alpha channel values are less than e. g. 50%). The question is then, what RGB values should be used for pixels which are discared? An easy solution is to use black or white or any other color because those pixels will not be visible anyway. And yes, those pixels will be discarded, but those RGB values are used to calculate mipmaps. When a white or black is used, artifacts can be observed on mipmaps (white or black halo around edges of the shape).
Figure 4: Example of white artifacts around grass blades
To avoid those artifacts a better solution is to use Solidify (see also Photoshop plug-in) or Dilation filter. In a nutshell, those filters add some edge padding using the color of the edge. This preserves the color of the edge down through the mipmap chain, which eliminates artifacts around the edges.
Figure 5: Example of original image on the left and the image after applying Dilation filter on the right
Figure 6: Example of original image on the left and the image after applying Solidify filter on the right
In combination, those two techniques can generate better quality of alpha tested materials. This can be shown in the examples presented in the videos below. In those videos on the far left is object with original texture, next to it is object with texture after applying fix 1, next to it is object with texture after applying fix 2. Finally on the far right is object with texture after applying fix 1 and 2.
Additionally, by using the presented techniques, the quality of shadow maps for alpha tested materials can increase. Why? For the same reason as described above. Alpha tested material doesn’t degenerate when rendered while the light (and its camera) is far away (for e. g. spotlight or cascaded shadow maps).
Figure 7: Close-up of an example of vegetation and its shadow. From left to right: original texture, texture after applying fix 1, texture after applying fix 2, after applying fix 1 and 2
Figure 8: Close-up of an example of a masking military mesh and its shadow. From left to right: original texture, texture after applying fix 1, texture after applying fix 2, after applying fix 1 and 2
What about the different types of filters that can be used to generate mipmaps? How does such a filter help to deal with problem with alpha tested materials? I prepared a test and generating mipmap filters available in Unity compared with the techniques shown in this text. In Unity, we can generate mipmap using two types of filters:
In Unity, there is an additional “Mip Maps Preserve Coverage” option, which, as we can read in the documentation, helps with the alpha tested materials issue.
I prepared another test case where the original image has been tested without the solutions proposed in this text and generated mipmaps with all those above options. Then I compared the results of the original image with two techniques proposed applied, with mipmap generation by Box and Kaiser filter (and without “Mip Maps Preserve Coverage”). The results of this test can be found in the video below.
As we can see, “Mip Maps Preserve Coverage” helps a lot with “disappearing” objects far from the camera, but two proposed techniques generate better visual results.
The conclusion is that using the proposed techniques shown in the text can improve the quality of alpha tested (cutout) materials. The first technique of remapping alpha values can be easily implemented in the game engine. The second technique can be applied by the artist in a graphic program designed for creating and processing raster graphics (Solidify or Dilation filter are part of these programs or can be easily found as plugins) or implemented in the game engine, but this is will be a rather rare case.
After publication, I received feedback (thanks Evgenii, Mikkel, Ignacio) with very good suggestions on how to improve this article. That's why I decided to update this article and add a new test case which, among others, tested various existing techniques.
The first suggestion I received was a comparison the presented technique with “ground truth”. What is “ground truth” in the context of cutout materials? After some thinking, I concluded that transparent material, in this case, is the best candidate for “ground truth”.
The second suggestion was to show a video of the test where an application is rendering with MSAA framebuffer (no AA techniques were used in earlier videos), so I decided to use what best rendering quality setting that Unity Universal Pipeline can offer. And I chose 4x Supersampling and 8x Multisampling Antialiasing setting. I know this is an exaggeration, but why not use it when the option is available.
The third suggestion was testing the presented technique against the Ignacio Castaño technique.
The fourth suggestion was testing the presented technique against the Hashed Alpha Testing technique. It is worth mentioning that this technique have the most computation overhead of all tested techniques..
Before I show video I want to say that all image mipmaps were generated using Kaiser filter for the individual techniques (of course when the technique uses mipmaping).
First, I want to show the alpha channel of images level 5 mipmap for four techniques:
- original texture
- original texture with Unity Mip Maps Preserve Coverage option
- original texture with Ignacio Castaño technique
- original texture with presented technique
And now, I want to present the comparative video mentioned earlier.
I am aware that the conclusion I am about to present is subjective. And I know that my conclusions are base on after watching only one test case (If someone has an idea what kind of objects should be tested, and can share data with me, please write info in the comments section). Nevertheless, the presented method looks the best for me. Compared to the second-best looking, cutout, and hashed alpha material without mipmaps it’s less noisy when the object is moving. Compare to transparent with mipmaps is sharper. And for the rest of the techniques, it preserves more “body” of rendered objects.
Presented technique unfortunately also has a disadvantage, after remapping alpha channel values, texture can not be used in transparent materials anymore.
Finnaly, I have another thought "what would happen if, and how would one combine the Ignacio Castaño technique or Unity with the one presented in the article? Will the results be even better? As you can see, the topics described in this article are just “the tip of the iceberg” and for me, improving the quality of cutout materials topic has been not solved. Who knows? Maybe there will be another update. I am always open to receive feedback from youŁukasz Izdebski Ph.D.