Transferred from: http://blog.csdn.net/candycat1992/article/details/41599167
Write in front
The question of Alpha has always been a more confusing thing, especially when it comes to translucency, and I don't know why A is blocking B, and B is in front of a. This article summarizes what I now know ~
The alpha test and alpha blending are two methods of processing transparency.
Alpha Test uses a very overbearing and extreme mechanism, as long as a pixel of alpha does not meet the conditions, then it will be fragment shader discard, "I do not want your class!" ”。 The discarded fragments will not have an effect on the various tests behind it, otherwise it will be written to the cache normally, with normal depth checking, and so on, i.e. Alpha test does not need to close zwrite. Alpha test produces extreme results, either completely transparent, invisible, or completely opaque.
The Alpha Blending is a way of moderation, which uses the current fragment alpha as a blending factor to mix the previously written color values in the cache. But the problem with alpha blending is that it needs to close zwrite and be very careful about the order in which objects are rendered. If the zwrite is not turned off, then the object behind it can be seen through the depth test, but it is removed because the depth is greater than its depth, so we cannot see the object behind it. Therefore, we need to make sure that the object's rendering order is from back to forward and closes the zwrite of the translucent object.
Note :Alpha blending just turns off Zwrite, and people don't have to close ztest Oh ! This means that when outputting an alpha blending fragment, it will still be judged and the depth of the fragment in the current color buffer is closed, and if it is farther than the current fragment depth, then it will not do any subsequent blending operations; It mixes with the current fragment, but does not write its own depth information into depth buffer. This is very important, which determines that even if an opaque object appears in front of a transparent object, the opaque object can still block the transparent object normally! In other words, for alpha blending, Depth buffer is read-only.
Surface Shader
Implementing both of these techniques is very simple in unity's surface shader, as described in the previous article--alpha test and Alpha Blending. Simply summarize, just set the Alphatest:_cutoff or Alpha command in the #pragma.
However, many children's shoes say that when using alpha blending not get the correct results, it is very likely that tags are not set, more specifically, the render queue is not set correctly. Generally transparent objects should be set at least as tags {"Queue" = "Transparent"}. Why do I have to set up the render queue correctly? If it is not set correctly, it is likely that the object behind the transparent object will appear in front of the transparent object.
But what is the rationale behind them? That starts with the vertex & Fragment shader they generate. So, take a look at the next section ~
Vertex & Fragment Shader
Let's start with a simpler Alpha Test.
In Vertex & Fragment shader, it's very easy to implement it.
One way is to write your own code in shader, as long as you use a statement like this:
[Plain]View Plaincopyprint?
- Alpha Test
- Clip (O.alpha-_cutoff);
The clip function is very simple to check whether its parameters are less than 0. If it is, call discard to discard the fragment, or leave it alone.
Another approach is to use the Alphatest directive for a fixed pipeline. The official documentation is visible. Use the alphatest instruction method to choose more, we not only judge it less than _cutoff discard the fragment, can also be judged whether it is greater than, is greater than equals, and so on. But the principle is the same as the first method, in the final analysis is to rely on the discard function to abandon the fragments that does not meet the criteria.
Alpha Blending is slightly more complicated because it involves some zwrite problems.
First, the render queue needs to be set up correctly:
[CSharp]View Plaincopyprint?
- Tags { "Queue" ="Transparent"}
Second, you need to close zwrite (but after you specify the following blending function, the deep cache is turned off behind):
[Plain]View Plaincopyprint?
- Zwrite OFF
We can then specify the blending function, similar to the following:
[Plain]View Plaincopyprint?
- Blend Srcalpha Oneminussrcalpha
These are the most common mixed function factors, and others can be found in the official documentation.
When using alpha blending, it is important to be careful about the problems that result from shutting down the depth cache. From this picture of unity, you can see:
The depth test is done behind the vertex shader, so in the fragment shader phase, because it shuts down the depth cache, the overlap of pixels depends entirely on the order in which they are rendered.
Note : There are child shoes in the comments that say the sequence diagram given in the OpenGL Wiki is culling and depth test behind fragment shader! Why did you run to the front again? There is a picture for the card (source OpenGL Wiki):
"Fragment Tests" is the place to do depth test. The child's shoes look very carefully. Yes, theoretically, it is really time to wait until the fragment shader is complete, and then perform various tests on all fragments. But modern GPUs tend to do something like "early-z" for performance reasons. This can be understood as an in-depth test before fragment shader to eliminate pixels that cannot be rendered, and these pixels are no longer called fragment shader for processing, which can improve performance. While the normal fs behind the depth test is generally still going to do, the previous "early-z" can be understood as a rough culling. In some cases, the GPU will do two times "Depth test" (for different GPUs, the first "Depth test" implementation may not be the same), but in some cases (such as alpha Test), the Depth before FS Test needs to be closed, and this will be discussed later.
As a result, unity gives the figure that the depth test in it should refer to the early-z result, but there will still be normal depth test behind this graph.
The above content is not guaranteed to be completely correct oh.
Of course, sometimes we can mix these two techniques, for example, the first pass uses alpha test to render the solid part, and the second pass uses the alpha blending to render the fragment in the previous pass.
Why render queue and render order is so important
Of course, this is what it says. Setting the render queue correctly refers to the policy when alpha blending. As previously stated, if you do not close the Zwrite, the object behind the Alpha blending can be seen through it, but because depth detection is greater than its depth it is removed, so we cannot see the object behind it. Therefore, we need to close the zwrite of the translucent object. So what does it matter with the render queue? If there is only one object in your scene, it is not important to render the queue. But once there are other opaque objects in the scene, the problem is in trouble. As the OpenGL wiki says, "first-the bad news." Really bad news ... Shutting down the deep cache poses a lot of trouble. "You're in big trouble!" "
There are two articles that I think you can look at: one is the OpenGL Wiki and one is a blog post on MSDN. Let me briefly explain why it is so much trouble to turn off the deep cache.
Let's begin by understanding why an object looks "translucent". In OpenGL, this is achieved through blending technology. We all know a thing called "Color Buffer," which can be understood as a variety of colors that we see on the screen. For opaque objects, the fragment after fragment shader is compared in depth with the fragment currently in color buffer, either overwriting it or discarding it. For translucent objects, however, because it turns off the depth cache, it does not make a deep comparison, but rather mixes the blend coefficients with the colors in the current color buffer so that the object looks as though it is seeing other objects through it.
Let's consider the following scenario (a blog from the source MSDN):
Where a object is translucent, and B is an opaque object. If we first render B and then a, then B will write to color buffer and depth buffer first. When you render A, a compares the first with the B in depth buffer, "I'm in front of you," and then blends correctly with the color of B in color buffer. However, if we first render a and then render B,a first writes color buffer, but does not write depth buffer. Note that there are no colors in the color buffer at this point, so a does not have a color blend to write to color buffer. When the b is rendered, B will do a normal depth test, it found that "ah, there is no one in the depth cache, then I am relieved to write color buffer ~", the result is B will cover the color of a. Visually, it seems that B appears in front of a (although the part of a that is not covered by B does look transparent).
This example shows how important the order of rendering is when a deep cache is closed. One of the simplest, and unity-based approach, is to ensure that all opaque objects are rendered before translucent objects . This is guaranteed by tags {"Queue" = "Transparent"}. Unity's queue tag determines the render queue for this object. For an opaque object, its "queue" = "Geometry", and for a translucent object, its "queue" = "Transparent". Objects in the geometry queue are always rendered before transparent, which ensures that all opaque objects are rendered before translucent objects. Therefore, if you do not set this value correctly, it is very likely that the above example will appear: the next B instead is in front of a.
The above method is simple and effective, but there are still problems in some complex situations. For example, we need to render two translucent objects before and after rendering. Or the above figure, this time A and B are translucent objects. Because both A and B are not written to the depth cache, the results depend entirely on their drawing order. If we first render B and then render A, then B is normally written to color buffer, and a is mixed with B in color buffer and the result is correct. However, if we first render a and then render B, then a writes to color buffer, then B mixes with a in color buffer, which looks like B in front of a and results in an error.
One way is to ensure that all translucent objects are rendered back and forth . This is also what unity does, as it says in the unity documentation:
Geometry Render Queue optimizes the drawing order of the objects for best performance. All other render queues sort objects by distance, starting rendering from the furthest ones and ending with the closest on Es.
That is, for the geometry queue rendering order is optimized within unity we are not aware of. But all other queues (including transparent) are sorted by the distance of the object, and then rendered in the order from far to near.
So you'll say, OK, it's all right. But still, there is a problem oh. If you think about it, "sort the distance of the object", what is the distance of the object? You would say, is the distance from the camera's Z-value, really annoying! However, since our sort is based on the entire object, rather than being sorted by pixel, as depth test. This means that the sort result is either object A is rendered in front of B, or a is rendered after B, but many times the real objects intersect. We can consider the following scenario (source OpenGL Wiki):
What do you think is the relative order of their previous results? The answer is that sorting through the entire object before will never get the correct result unless we divide each object into two parts by occlusion.
See here you will say, divided into two parts of the total good! There's no problem! But ..... Yes, there are so many but. Even if we promise that there will be no such thing as a circular occlusion, there is still a problem. Let's consider the following scenario (source OpenGL Wiki):
The question here is, "How to sort?" "We know that the grid structure of an object tends to occupy an area of space, which means that the depth value of each point on this grid may be different, and which depth value we use as the depth of the whole object to sort the other objects?" Grid midpoint? The farthest point? The nearest point? Unfortunately, none of them is right. For example, if you sort by using the depth value of the midpoint in the grid, then B is in front of C, but in fact B is partially obscured by C. Similarly, using the farthest point and the nearest point can not guarantee the result is correct. the method in unity is to use the center point of the grid to sort the translucent objects . This means that, in some cases, there must be a false occlusion before a translucent object. For example, this man has encountered such a problem. If this is the case, then the workaround is also to split the grid. In fact, any such a has a part on the B, and B has a part of the problem of a above, if the zwrite is closed again, probably only the method of dividing the grid.
You would say, split the grid is troublesome, and in the case of moving the model, and then re-partition, there is no other way? Of course, there are some drawbacks to other methods, which means we have to weigh them. All of the above issues are due to the consequences of shutting down the Zwrite (you now know how scary it is). Well, let's just start it up. The alpha test mentioned earlier does not have the zwrite turned off, so we can use alpha test instead, but the downside is that we don't get the smoothed boundary of translucency (and the consequences of performance degradation on the mobile platform). Another way is to turn on zwrite. The Unity document gives an example of using a pass to render the depth information before using the alpha blending for transparent rendering, since alpha blending does not write to depth Buffer. It determines whether to mix according to the results of the previous pass. But the disadvantage of this approach is that it only looks like a transparent object, but it does not reveal the color of the object behind it.
Thus, we summarize the following considerations for translucent object rendering:
- For rendering relationships between opaque objects and translucent objects, unity guarantees the correctness of the rendering order through the queue, so we must correctly set the "queue" = "Transparent" of the translucent object.
- For rendering relationships between translucent objects and translucent objects, unity uses a grid center point to sort + render from back to front to ensure that the rendering order is as accurate as possible. However, for partially occluded objects, there is still an incorrect occlusion effect. So we either split the grid or use alpha test or open zwrite instead.
Performance
In the official document of unity, there are two references to their performance issues-one that is performance hints when writing shaders, and one that optimizes image performance. Slightly feel the two pages have a lot of repetition, one day in the future may be composed of a page ...
That's how they write:
Fixed function alphatest or it's programmable equivalent, clip()
with different performance characteristics on different pl Atforms:
- Generally it's a small advantage to use it for cull out totally transparent pixels on the most platforms.
- However, on PowerVR GPUs found in IOS and some Android devices, alpha testing is expensive. Don't try to use it as "performance optimization" there, it'll be slower.
And
Keep in mind that alpha Test (Discard) operation'll make your fragments slower.
To summarize, it may seem simpler to use alpha test, but on most platforms there is only a little bit of performance improvement over the alpha Blending. But!!! On iOS and some Android devices, because they use PowerVR GPUs, the performance of alpha test is more expensive. Therefore, one caveat is to use Alpha Blending as much as possible instead of using Alpha Test.
We would find it strange to not close the depth cache, do not need to calculate the mixed color, just call to discard discard fragment is not very simple? Why is it less efficient on a mobile platform? There is a saying, "Simple rough", can be used here. The reason for the decline in performance is that it's too rough! (I don't want to take it seriously ... )
OK, so to go to the reason, is the previous two times mentioned test. Because I have limited experience, I can only rely on strong Google to find the answer. I found here, here. To summarize, it is PowerVR GPUs used a technique called "Deferred tile-based-rendering". There is an optimization phase in this technique, in order to reduce overdraw it will determine which tiles are actually rendered before calling fragment shader. Just say what we said before FS "Depth Test". However, because Alpha test uses the clip function in fragment shader to change the result of the fragment being rendered, GPUs cannot use the optimization strategy described above. In other words, as long as all the fragment shader processing is completed, GPUs know which fragments will be actually rendered to the screen, so that the original can reduce the overdraw of the optimization is not valid.
(GO) "Unity Shaders" alpha Test and Alpha Blending