{"id":1535,"date":"2020-03-30T20:33:35","date_gmt":"2020-03-30T12:33:35","guid":{"rendered":"http:\/\/blog.coolcoding.cn\/?p=1535"},"modified":"2020-09-22T15:49:01","modified_gmt":"2020-09-22T07:49:01","slug":"anti-aliased-alpha-test-the-esoteric-alpha-to-coverage","status":"publish","type":"post","link":"https:\/\/blog.coolcoding.cn\/?p=1535","title":{"rendered":"[Unity]Anti-aliased Alpha Test: The Esoteric Alpha To Coverage[1]"},"content":{"rendered":"\n<p>ref: <a href=\"https:\/\/medium.com\/@bgolus\/anti-aliased-alpha-test-the-esoteric-alpha-to-coverage-8b177335ae4f\">https:\/\/medium.com\/@bgolus\/anti-aliased-alpha-test-the-esoteric-alpha-to-coverage-8b177335ae4f<\/a> <\/p>\n\n\n\n<p>AlphaToMask on<\/p>\n\n\n\n<p>Aliasing is the bane of VR. We have several tools we use to try to remove, or at least reduce aliasing in real time graphics today. Tools like&nbsp;<a rel=\"noreferrer noopener\" href=\"https:\/\/en.wikipedia.org\/wiki\/Supersampling\" target=\"_blank\">super sampling<\/a>,&nbsp;<a rel=\"noreferrer noopener\" href=\"https:\/\/en.wikipedia.org\/wiki\/Temporal_anti-aliasing\" target=\"_blank\">temporal anti-aliasing (TAA)<\/a>, and&nbsp;<a rel=\"noreferrer noopener\" href=\"https:\/\/en.wikipedia.org\/wiki\/Multisample_anti-aliasing\" target=\"_blank\">multi sample anti-aliasing (MSAA)<\/a>. They all have their pros and cons, but there\u2019s one tool I don\u2019t feel like gets the use and respect it deserves. That tool is a feature of MSAA, Alpha to Coverage.<\/p>\n\n\n\n<p>This article will be assuming you\u2019re using forward rendering with at least 4x MSAA. If you\u2019ve chosen to use deferred rendering this article will be less useful for you. Some techniques are still applicable though. If you\u2019ve chosen to use TAA and forward rendering this may solve one of the reasons you chose that option!<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"fdd0\">The Problem<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"e7a4\">Self Inflicted Aliasing<\/h2>\n\n\n\n<p>A very common case of aliasing is the use of alpha test, used for things like trees or bushes. Lets look at why it\u2019s used so much even though it causes so much aliasing.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"640\" height=\"480\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/1_8EKqWSOOPXaTrDHVFTACJg.png\" alt=\"\" class=\"wp-image-1536\" srcset=\"https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/1_8EKqWSOOPXaTrDHVFTACJg.png 640w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/1_8EKqWSOOPXaTrDHVFTACJg-300x225.png 300w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/1_8EKqWSOOPXaTrDHVFTACJg-67x50.png 67w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/figure>\n\n\n\n<p>This is a basic plant mesh in Unity with a very basic lit alpha test shader. In Unity these are often called Cutout shaders. I\u2019m using the bush03 mesh and texture from&nbsp;<a href=\"https:\/\/www.assetstore.unity3d.com\/en\/#!\/content\/52977\" target=\"_blank\" rel=\"noreferrer noopener\">Nature Starter Kit 2<\/a>&nbsp;on the asset store if you want to try this yourself. It doesn\u2019t look to bad, but there\u2019s a lot of aliasing there from the alpha testing. Note that the above image&nbsp;<em>is&nbsp;<\/em>using 4x MSAA. That doesn\u2019t help here since this is shader aliasing which generally MSAA can\u2019t help with. This is something people usually fix with some extra super sampling or they give up on MSAA and use TAA instead. Or they just live with it.<\/p>\n\n\n\n<p>What about traditional alpha blending? Seems like that should be an obvious answer, so why don\u2019t people use that?<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"640\" height=\"480\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/222.png\" alt=\"\" class=\"wp-image-1537\" srcset=\"https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/222.png 640w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/222-300x225.png 300w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/222-67x50.png 67w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/figure>\n\n\n\n<p> That looks way better, at least in this static shot, maybe a little blurry, but the aliasing is gone! Success, right? Of course it isn\u2019t that easy. In motion using alpha blending for this case looks&nbsp;<em>really&nbsp;<\/em>wrong. <\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"300\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/333.gif\" alt=\"\" class=\"wp-image-1538\"\/><\/figure>\n\n\n\n<p> It almost looks like the bush is inside out! It\u2019s even more obviously wrong in VR, so this isn\u2019t a usable option. So what\u2019s happening here? Alpha blended shaders don\u2019t get to use per pixel depth sorting like opaque and alpha test shaders do. The bush is made from a bunch of intersecting polygons. You could sort the polygons, but they\u2019re still intersecting so that only helps so much. So, that\u2019s why we\u2019re stuck with using alpha test. <\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"640\" height=\"480\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/444.png\" alt=\"\" class=\"wp-image-1539\" srcset=\"https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/444.png 640w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/444-300x225.png 300w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/444-67x50.png 67w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/figure>\n\n\n\n<p>But there&nbsp;<em>are&nbsp;<\/em>ways to have the benefit of both alpha test and alpha blend; a way to get per pixel depth sorting and the smooth edge. There are some two pass techniques<a href=\"https:\/\/medium.com\/@bgolus\/anti-aliased-alpha-test-the-esoteric-alpha-to-coverage-8b177335ae4f#620e\">\u00b9<\/a>&nbsp;that can work well. There are also some order independent transparency approximations, though they are potentially expensive. There\u2019s an even better option if you\u2019re using MSAA. Enter Alpha to Coverage.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"d966\">The Solution<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"41c7\">This One Simple Trick \u2026<\/h2>\n\n\n\n<p>Alpha to Coverage (sometimes referred to as A2C or ATOC) is a less commonly mentioned graphics technique. While used by several big games, it is seemingly unknown to the vast majority of smaller devs. It\u2019s a powerful tool that is often overlooked due to the requirement of MSAA. Games often treat MSAA as an expensive option for high end PCs. Or they\u2019ve used rendering techniques that don\u2019t allow for easy use of MSAA to begin with, like deferred rendering. The result is games have overwhelmingly preferred post process anti-aliasing like&nbsp;<a href=\"https:\/\/en.wikipedia.org\/wiki\/Fast_approximate_anti-aliasing\" target=\"_blank\" rel=\"noreferrer noopener\">FXAA<\/a>. This made Alpha to Coverage even more uncommon for a while. But the rise of VR has lead to the resurgence of forward rendering and of MSAA. This means A2C has become a useful tool again, especially for VR.<\/p>\n\n\n\n<p>Anti-aliased alpha test is one of the most basic use cases for Alpha to Coverage. The basic idea for this has been around for as long as A2C has existed. It may even have been the original intended use for it. The first time I saw this idea was on Emil Persson\u2019s (better known to many as Humus) site&nbsp;<a href=\"http:\/\/www.humus.name\/index.php?page=3D&amp;ID=61\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a>. I believe the method in this article is superior to the one used in his original demo.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"d276\">Multi Sample Anti-Aliasing<\/h2>\n\n\n\n<p>Before we get into that though, I\u2019m going to quickly run down what Alpha to Coverage&nbsp;<em>is<\/em>&nbsp;and why MSAA is a requirement.<\/p>\n\n\n\n<p>Alpha to Coverage maps the alpha output from a pixel shader to the coverage mask of MSAA. Obvious, right?!<\/p>\n\n\n\n<p>Okay, that\u2019s going to take a little more explanation. Be warned, I\u2019m going to get into some of the gritty details here.<\/p>\n\n\n\n<p>The big trick with MSAA and how it differs from super sampling is what Multi Sample Anti-Aliasing is doing multiple samples&nbsp;<em>of<\/em>. Super sampling at its most basic is rendering&nbsp;<em>everything&nbsp;<\/em>at a higher resolution. The final on screen image is a scaled down average of this higher resolution render. MSAA renders the color at the same resolution as the target resolution, but multiple&nbsp;<em>coverage&nbsp;<\/em>samples per pixel. In simpler terms it\u2019s rendering the depth of the scene at a higher resolution than the target resolution. Each rendered triangle\u2019s depth samples are its coverage. If you\u2019re using 4x MSAA it\u2019s rendering 4 depth \/ coverage samples within the bounds of each pixel. Each coverage sample can store a unique color. But it\u2019s only getting a color by running the pixel shader once&nbsp;<em>per triangle<\/em>, per pixel. Most of time all 4 depth samples will be the same triangle. In this case all 4 coverage samples use the same single pixel shader output color. This behaves no different than when having no MSAA enabled at all.<\/p>\n\n\n\n<p>Where MSAA\u2019s strength comes into play is when a triangle doesn\u2019t cover all the depth samples. For example, if a triangle only covers two depth samples the two matching coverage samples are set to the pixel shader ouput. The other coverage samples can store the color of other triangles\u2019s pixel shaders that can be seen.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"234\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/555-1024x234.png\" alt=\"\" class=\"wp-image-1540\" srcset=\"https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/555-1024x234.png 1024w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/555-300x68.png 300w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/555-768x175.png 768w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/555-70x16.png 70w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/555.png 1030w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>\n\nfrom&nbsp;<a href=\"https:\/\/mynameismjp.wordpress.com\/2012\/10\/24\/msaa-overview\/\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/mynameismjp.wordpress.com\/2012\/10\/24\/msaa-overview\/<\/a><\/p>\n\n\n\n<p>More in depth write ups can be found elsewhere, like on Matt Pettineo\u2019s (MJP) site.<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-link is-provider-the-danger-zone\"><div class=\"wp-block-embed__wrapper\">\n<a href=\"https:\/\/mynameismjp.wordpress.com\/2012\/10\/24\/msaa-overview\/\">A Quick Overview of&nbsp;MSAA<\/a>\n<\/div><\/figure>\n\n\n\n<p>I\u2019m glossing over a bunch of details, but the main point is MSAA can store a list of multiple colors per pixel, one color per coverage sample. These get averaged together to make the final on screen pixel color. But it\u2019s not always having to render a pixel shader more than once per pixel resulting in significant savings over super sampling of similar quality.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"1606\">Alpha to Coverage<\/h2>\n\n\n\n<p>This is where Alpha to Coverage comes in. Alpha to Coverage let\u2019s the pixel shader\u2019s output alpha change the fraction of coverage samples rendered to. Hence the name. In the simple case of a triangle covering all 4 depth samples, all 4 coverage samples store the color output. With Alpha to Coverage enabled and an output color alpha of 0.5, only half of the possible coverage samples will store the color. The result will be as if it\u2019s half<a href=\"https:\/\/medium.com\/@bgolus\/anti-aliased-alpha-test-the-esoteric-alpha-to-coverage-8b177335ae4f#5995\">\u00b2<\/a>&nbsp;as opaque as it\u2019s only contributing color to half the samples!<\/p>\n\n\n\n<p>Okay, so with 4x MSAA and Alpha to Coverage you get 5 levels of opacity you can play with, 4\/4 through 0\/4 samples. What good is that? With traditional alpha blending you get at least 256 levels, so why would you ever want to use this? Because with Alpha to Coverage those coverage samples are opaque. That means they can be depth sorted! You can have a mesh of polygon soup and have intersecting triangles accurately sort at better than per pixel accuracy. This is no different than opaque objects or even alpha test with MSAA, but you also get soft edges.<\/p>\n\n\n\n<p>Lets just turn Alpha to Coverage on and see how that looks. In Unity\u2019s ShaderLab it\u2019s enabled by just adding&nbsp;<code>AlphaToMask On<\/code>&nbsp;to an otherwise opaque shader that outputs alpha. So here we go \u2026<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"640\" height=\"480\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/777.png\" alt=\"\" class=\"wp-image-1541\" srcset=\"https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/777.png 640w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/777-300x225.png 300w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/777-67x50.png 67w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/figure>\n\n\n\n<p>And we\u2019re done, doesn\u2019t that look so much better than before?! Lets compare it to the original alpha test version!<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"640\" height=\"480\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/8.png\" alt=\"\" class=\"wp-image-1542\" srcset=\"https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/8.png 640w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/8-300x225.png 300w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/8-67x50.png 67w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"640\" height=\"480\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/9.png\" alt=\"\" class=\"wp-image-1543\" srcset=\"https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/9.png 640w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/9-300x225.png 300w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/9-67x50.png 67w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/figure>\n\n\n\n<p> Wait, no, Alpha to Coverage looks terrible! It\u2019s somehow simultaneously fuzzy and aliased, not to mention sort of noisy, and seems like the bush got more transparent through the middle? The story only gets worse when we get close up to those leaves <\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"640\" height=\"480\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/10.png\" alt=\"\" class=\"wp-image-1544\" srcset=\"https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/10.png 640w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/10-300x225.png 300w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/10-67x50.png 67w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/figure>\n\n\n\n<p>Oy vey, that\u2019s no good at all. The limited number of opacity steps makes for some ugly banding. Clearly there\u2019s a reason why this isn\u2019t used more often, so why would I even suggest doing this if it looks so bad?!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"c66b\">Fixing the Solution<\/h2>\n\n\n\n<p>Well, because that\u2019s what happens if you use Alpha to Coverage with the alpha of the texture straight. This is what replacing basic alpha blending using alpha to coverage looks like. Many implementations of Alpha to Coverage I\u2019ve seen in games really do leave it here.<\/p>\n\n\n\n<p>But what\u2019s happening in that first alpha to coverage image to make it so bad? It\u2019s not just the banding, it\u2019s also the interaction between the coverage samples. In short it\u2019s a limitation of using Alpha to Coverage. Essentially when using Alpha to Coverage only \u201cone layer\u201d of a specific coverage can be seen at one time. Two or more overlapping triangles with 50% Alpha to Coverage results in no change in opacity. Only the closest coverage points get rendered. This is unlike alpha blending where they will blend over each other increasing the perceived opacity. If you remember earlier I said the coverage samples are individually opaque. If the output alpha is 50% with 4x MSAA only two coverage samples are written to. But it\u2019s going to be the&nbsp;<em>same<\/em>&nbsp;two coverage samples for everything that renders to that pixel with 50% A2C! The answer might seem like \u201cwell, render to different samples!\u201d. But that\u2019s harder than it sounds to do and be effective. It is an option though. Or you might be thinking \u201cnope, still don\u2019t get it at all\u201d, and that\u2019s okay. Don\u2019t worry about it. It\u2019s unnecessary to understand for this particular case as it\u2019s relatively easy to solve.<\/p>\n\n\n\n<p>So what is the solution here? Alpha to Coverage is great for anti-aliasing alpha&nbsp;<em>test<\/em>, not so much for replacing an alpha blend! We need to sharpen the alpha so there are fewer overlapping areas of partial opacity. This will simultaneously solve the banding, and interior transparency. Anyone who\u2019s done signed distance field text, or shader based line rendering probably knows whats coming next. For everyone else I\u2019m going to introduce you to one of my favorite functions in shaders:<\/p>\n\n\n\n<p><code>fwidth()<\/code><\/p>\n\n\n\n<p>With out further explanation we get this (I\u2019ll talk about it later)<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"640\" height=\"480\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/11.png\" alt=\"\" class=\"wp-image-1545\" srcset=\"https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/11.png 640w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/11-300x225.png 300w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/11-67x50.png 67w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"640\" height=\"480\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/12.png\" alt=\"\" class=\"wp-image-1546\" srcset=\"https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/12.png 640w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/12-300x225.png 300w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/12-67x50.png 67w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/figure>\n\n\n\n<p> Now that\u2019s better. Looks very similar to the original alpha tested version, but is nicely anti-aliased. And it\u2019s not strangely transparent, noisy, or blurry anymore! Here\u2019s the original alpha tested version one more time for reference <\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"640\" height=\"480\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/13.png\" alt=\"\" class=\"wp-image-1547\" srcset=\"https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/13.png 640w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/13-300x225.png 300w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/13-67x50.png 67w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"640\" height=\"480\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/14.png\" alt=\"\" class=\"wp-image-1548\" srcset=\"https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/14.png 640w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/14-300x225.png 300w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/14-67x50.png 67w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/figure>\n\n\n\n<p>So, what did I do? What was that&nbsp;<code>fwidth()<\/code>&nbsp;I mentioned earlier? All the magic is in this one line of shader code.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">col.a = (col.a - _Cutoff) \/ max(fwidth(col.a), 0.0001) + 0.5;<\/pre>\n\n\n\n<p>What that line does is sharpens the alpha of the texture to the width of a single pixel. The&nbsp;<code>fwidth()<\/code>&nbsp;function returns the sum of the pixel\u2019s partial derivatives for a value, usually described as the equivalent of&nbsp;<code>abs(ddx(value)) + abs(ddy(value))<\/code>. Those two functions,&nbsp;<code>ddx()<\/code>&nbsp;and&nbsp;<code>ddy()<\/code>, are partial derivative functions. They measure the change of a value between the two pixels either to the side or above<a href=\"https:\/\/medium.com\/@bgolus\/anti-aliased-alpha-test-the-esoteric-alpha-to-coverage-8b177335ae4f#68b9\">\u00b3<\/a>&nbsp;the pixel that\u2019s currently being processed. Derivatives are wonderful, magical things in shaders. For the moment it\u2019s enough to know&nbsp;<code>fwidth()<\/code>&nbsp;gives a good approximation of how much a value changes per pixel. If you divide a value by how much it\u2019s changing it\u2019ll rescale it to give you a nice crisp edge. The&nbsp;<code>max()<\/code>&nbsp;is there because if the texture\u2019s alpha is a flat color at some point, like black or white,&nbsp;<code>fwidth()<\/code>&nbsp;will be zero. Dividing by zero is bad! The 0.0001 is just an arbitrarily small number, small enough that you have to really get close to a texture before the banding will show up again. The&nbsp;<code>_Cutoff&nbsp;<\/code>is the same value you would be using for an alpha test shader and helps center the alpha around the edge you want.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"200\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/15.png\" alt=\"\" class=\"wp-image-1549\" srcset=\"https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/15.png 600w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/15-300x100.png 300w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/15-70x23.png 70w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><\/figure>\n\n\n\n<p>\n\nWe could end here, but another problem that will show up that most people will probably miss initially. It is an issue that plagues alpha test already, so people might just be living with it. As you get far away from an object using alpha test it\u2019ll appear to shrink and eventually vanish. Alpha blending does not suffer from this\n\n<\/p>\n\n\n\n<p> We could end here, but another problem that will show up that most people will probably miss initially. It is an issue that plagues alpha test already, so people might just be living with it. As you get far away from an object using alpha test it\u2019ll appear to shrink and eventually vanish. Alpha blending does not suffer from this <\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"780\" height=\"220\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/16.gif\" alt=\"\" class=\"wp-image-1550\"\/><\/figure>\n\n\n\n<p> The cause is the texture\u2019s mip mapping making the alpha fade out from averaging. Since the alpha is fading out that hard \u201ccutoff\u201d edge is vanishing too <\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"256\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/17.png\" alt=\"\" class=\"wp-image-1551\" srcset=\"https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/17.png 768w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/17-300x100.png 300w, https:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/03\/17-70x23.png 70w\" sizes=\"(max-width: 768px) 100vw, 768px\" \/><\/figure>\n\n\n\n<p> There are several workarounds to this problem. Lowering the cutoff value (a common solution for alpha testing in general). Super sampling in the shader (I do this for font rendering). Or using some kind of noise like stochastic sampling or&nbsp;<a rel=\"noreferrer noopener\" href=\"https:\/\/casual-effects.com\/research\/Wyman2017Hashed\/index.html\" target=\"_blank\">hashed alpha testing<\/a>. Probably the best solution is the one proposed by Ignacio Casta\u00f1o on The Witness blog back in 2010. It requires recalculating each mip maps\u2019 alpha values in a way that\u2019s aware of it\u2019s intended use for alpha testing or Alpha to Coverage. The Witness uses A2C for foliage as well as several other effects too. <\/p>\n\n\n\n<p><a rel=\"noreferrer noopener\" href=\"http:\/\/the-witness.net\/news\/2010\/09\/computing-alpha-mipmaps\/\" target=\"_blank\">Computing Alpha MipmapsA little problem that we had when we started to create trees and vegetation is that the standard mipmap generation\u2026the-witness.net<\/a><\/p>\n\n\n\n<p><blockquote class=\"wp-embedded-content\" data-secret=\"J47v2qGKgv\"><a href=\"http:\/\/the-witness.net\/news\/2010\/09\/computing-alpha-mipmaps\/\">Computing Alpha Mipmaps<\/a><\/blockquote><iframe title=\"&#8220;Computing Alpha Mipmaps&#8221; &#8212; The Witness\" class=\"wp-embedded-content\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; clip: rect(1px, 1px, 1px, 1px);\" src=\"http:\/\/the-witness.net\/news\/2010\/09\/computing-alpha-mipmaps\/embed\/#?secret=J47v2qGKgv\" data-secret=\"J47v2qGKgv\" width=\"600\" height=\"338\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\"><\/iframe><\/p>\n\n\n\n<p>It has the great advantage of being \u201cfree\u201d since all of the work is done before hand and there\u2019s nothing the shader has to do. For Unity it could be implemented with an asset post processor or an editor script.<\/p>\n\n\n\n<p><em>Edit: S\u00e9bastien Lagarde brought to my attention the fact that Ignacio\u2019s technique is now available as an official texture import option in Unity 2017.1! There\u2019s now a Mip Maps Preserve Coverage check box.<\/em><\/p>\n\n\n\n<p>But there is another way that works almost as well, scaling the alpha by mip level. Effectively that\u2019s what the above solution is doing, so we can just do an approximation the shader. It\u2019s not \u201cfree\u201d, but it\u2019s not that expensive either.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">col.a *= 1 + CalcMipLevel(i.uv * _MainTex_TexelSize.zw) * _MipScale;<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>ref: https:\/\/medium.com\/@bgolus\/anti-aliased-alpha-test [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[18],"_links":{"self":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/1535"}],"collection":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1535"}],"version-history":[{"count":5,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/1535\/revisions"}],"predecessor-version":[{"id":2596,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/1535\/revisions\/2596"}],"wp:attachment":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1535"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1535"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1535"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}