Original address: Unity shader-render Queue, ztest,zwrite,early-z
Brief introduction
In the rendering phase, the engine's job is to render objects in all scenes in a certain strategy (order). The earliest is the painter algorithm, as the name implies, is like a painter painting, the first painting behind the object, if there are objects, then the object in front of the object is covered out, but this way because the order is for objects to sort, and objects may overlap, so the effect is not good. So the more commonly used way is the z-buffer algorithm, similar to the color buffer buffer color, z-buffer is stored in the current depth of information, for each pixel to store a depth value, so that we display on the screen each pixel will be depth sorting, You can guarantee that the occlusion relationship is correct. and control Z-buffer is through Ztest, and zwrite to carry on. But sometimes it takes more precision to control the rendering order of different types of objects, so there is a render queue. Learn about the render queue today, Ztest,zwrite basic usage, and analyze some of the optimizations that unity has done for early-z.
Several render queues in unity
First look at several of the built-in render queues in unity, according to the order of rendering, binate to after sorting, the smaller the number of queues, the more the first rendering, the larger the number of queues, the more the later rendering.
Background (1000) The queue of the object that was first rendered.
Geometry (2000) render queue for opaque objects. Most objects should be rendered using this queue and are the default render queue in unity shader.
The Alphatest (2450) has a transparent channel, and the queue of objects that require alpha test is more efficient than in geomerty.
Transparent (3000) a render queue for a semi-transparent object. Objects that are not written in depth, such as Alpha blend, are generally rendered in this queue.
Overlay (4000) The queue of the last rendered object, typically covering effects such as lens flares, screen patches, and so on.
Setting up the render queue in unity is also very simple, we do not need to create manually, and do not need to write any script, only need to add a tag in the shader, of course, if not added, then the default render queue geometry. For example, if we want our objects to be rendered in transparent this render queue, we can write:
[CSharp] View plain copy
Tags {"Queue" = "Transparent"}
We can see the shader's render queue directly on the Shader Inspector panel:
In addition, we often write shader when there is a tag called rendertype, but this is not a render queue so common, here by the way record:
Opaque: For most shaders (normal shader, self-light shader, reflection shader, and terrain shader).
Transparent: Shader for translucent shaders (transparent shader, particle shader, font shader, Terrain extra channel).
Transparentcutout: Skin Transparent Shader (Transparent cutout, two-channel vegetation shader).
Background: Sky Box shader.
Overlay:guitexture, lens flare, screen flash and other effects use shaders.
Treeopaque: The bark in the terrain engine.
Treetransparentcutout: Leaves in the terrain engine.
Treebillboard: The billboard tree in the terrain engine.
Grass: Grass in the terrain engine.
Grassbillboard: What is the billboard grass in the terrain engine?
Render order of opaque objects in the same render queue
Take out unity and create three cubes, all using the default bump diffuse shader (the same render queue), give three different materials (the object engine with the same material's small vertex count is dynamic), and use the Frame Debug tool with Unity5 to view the draw Call. (Unity5 is really useful, if you use 4, you have to use nsight, such as grab frames)
As you can see, the opaque objects in unity are rendered in the pre-to-post rendering order, so that the opaque object is carried out in the vertex phase, Z Test, and then you can get the object is finally visible on the screen, if the previous rendered object has been written depth, The depth test fails, so the object that is rendered later will not go through the fragment phase directly. (however, the distance between the three objects should be slightly pulled apart, I found in the test, if the distance is particularly near, there will be more chaotic rendering order, because we do not know what the unity within the specific sort of what criteria to determine which object is closer to the camera, here I do not speculate)
The rendering order of semitransparent objects in the same render queue
Transparent object rendering has always been a graphical aspect of the egg pain, for the rendering of transparent objects, can not be like rendering opaque objects as M.F.B. s, because transparent objects do not write depth, that is, transparent objects between the interspersed relationship is no way to judge, So translucent objects in the rendering is generally used from the back to the method of rendering, because transparent objects more, transparent objects do not write depth, then there is no so-called transparent objects can be removed through the depth of the optimization, each transparent object will walk the pixel stage of the rendering, will cause a lot of over Draw. This is why particle effects are particularly cost-effective.
Let's experiment with the order in which the translucent objects are rendered in unity, or the three cubes above, we change the shader of the material into the shader of the most common particle/additive types of particles, and then use the Frame Debug tool to see the Order of rendering:
Translucent objects are rendered in the order from the back to the front, but because the content of the semi-transparent related more complex, first not in this article said, intends to start another article.
Custom Render Queues
Unity allows us to customize the render queue, for example, we need to make sure that some type of object needs to be rendered after other types of objects are rendered, and can be rendered by customizing the render queue. And super handy, we just need to modify the tag in the render queue when writing shader. For example, we want our object to render after all the default opaque objects have been rendered, so we can use tag{"Queue" = "Geome www.yigozongdai2.cn/try+1"} You can let the object using this shader be rendered in this queue.
Or the above three cubes, this time we give three different shader, and the rendering queue is different, through the above experiment we know that by default, opaque objects are rendered in this queue geometry, then opaque three objects will follow Cube1,cube2, Cube3 for rendering. This time we want to turn the order of rendering back, so we can make the CUBE1 render queue the largest, cube3 render queue the smallest. Post one of the shader:
[CSharp] View plain copy
Shader "Custom/renderqueue1" {
Subshader
{
Tags {"Rendertype" = "Opaque" "Queue" = "geometry+1"}
Pass
{
Cgprogram
#pragma vertex vert
#pragma fragment Frag
#include "Unitycg.cginc"
struct V2F
{
FLOAT4 pos:sv_position;
};
v2f Vert (appdata_base www.t1yl1.com/v)
{
v2f o;
O.pos = Mul (UNITY_MATRIX_MVP, V.vertex);
return o;
}
Fixed4 Frag (v2f i): Sv_target
{
Return Fixed4 (0,0,1,1);
}
Endcg
}
}
FallBack "Diffuse"
}
The other two shader are similar, except that the render queue and output colors are different.
By rendering the queue, we are free to control when the object using the shader is rendered. For example, if the pixel stage operation of an opaque object is more cost-intensive, we can control its render queue so that it renders more so that it can be removed by the depth of the other opaque object's writing.
PS: Here seems to find a problem, we generally do not need any other operation when modifying shader can directly see the changes, but after I change the render queue, sometimes appear from the shader file can see the changes in the render queue, but from the rendering results and frame The debug tool did not see a change in the rendering results, and restarting unity did not work until I re-assigned the shader to the material, and the change only worked ... (Guess is a bug, because see online and I like the unlucky egg was this hole, my version is 5.3.2, harm I almost suspected yesterday is not drink, just finished the result is completely wrong ... )
ZTest (depth test) and zwrite (deep write)
In the previous example, although the order of rendering is reversed, the occlusion relationship between objects is still correct, which is the credit of Z-buffer, regardless of our rendering order, the occlusion relationship remains correct. And our call to Z-buffer is achieved through ztest and zwrite.
First look at the ztest,ztest is the depth test, the so-called test, is the current object on the screen (more precisely frame buffer) corresponding to the pixel point, the object's own depth value and the current pixel cache depth value comparison, if passed, This object will not write color to the color buffer at that pixel, otherwise it will not be written to the color buffer. The ztest provides more states. ZTest less (the depth is smaller than the current cache passes, ZTest Greater (the depth is greater than the current cache is passed), ZTest lequal (the depth is less than or equal to the current cache pass), ZTest gequal (depth greater than or equal to the current cache is passed), ZTest Equal (depth equals current cache pass), ZTest notequal (depth is not equal to current cache pass), ZTest always (regardless of how it is passed). Note that ZTest off is equivalent to ZTest always, and closing the depth test equals full pass.
Here's a look at the zwrite,zwrite is simpler, there are only two states, Zwrite on (Turn on depth write) and zwrite off (turn deep write off). When we turn on depth writing, the object is rendered with the depth of each pixel on the screen (more precisely the frame buffer) written to the depth buffer, whereas if it is zwrite Off, the depth of the object is not written to the depth buffer. However, whether the object will write depth, in addition to zwrite this state, more important is the need for deep testing through, that is, ztest through, if the ztest did not pass, then will not write depth. Just like the default rendering state is Zwrite www.6788878.cn/On and ztest lequal, if the current depth test fails, indicating that the pixel corresponds to the location, there is already a more front of the thing occupied the pit, even if written, there is no original more forward, Then there is no need to write the depth again. So the above ztest is divided into through and not through two cases, zwrite divided into open and close two cases, altogether is four kinds of situations:
1. Deep test pass, deep write open: Write depth buffer, write color buffer;
2. Deep test pass, deep write close: Do not write depth buffer, write color buffer;
3. Depth test failure, deep write on: Do not write depth buffer, do not write color buffer;
4. Depth test failed, deep write closed: No depth buffer, no color buffer;
The default state in Unity (the state that writes shader when nothing is written) is Ztest lequal and zwrite on, which means that deep write is turned on by default, and depth is less than or equal to the depth of the current cache through deep testing, the original in the depth cache is infinitely large, This means that objects closer to the camera update the depth cache and block the objects behind. As shown, the front square realizes that the object behind is obscured:
Write a few simple examples to see how the Ztest,zwrite and the render queue control the rendering results.
Let the green object not be obscured by the front cube, one way is to turn off the front blue cube depth write:
Through the above experimental results, we know that in the pre-to-post rendering order, the first rendering of the Blue object, the Blue object depth test through, the color write cache, but closed the deep write, the blue part of the depth cache value is still the default max, the Green cube behind the rendering, the depth test will still succeed, The color cache is written and the depth is written so that the Blue Cube does not act as a mask.
Another way is to have the Green Force pass the depth test:
In this example, the shader of the other cubes use the default rendering method, the green ztest is set to always, that is, in any case, the depth test through, the color of the green cube is written to the cache, if there is no other overwrite, then the final output is green.
So what if the red ones open ztest always?
In the Red cube also used ztest always, red obscured the green part of the display for red. If we change the render queue so that green is rendered before red, the result is different:
The render queue has been replaced, the green render queue +1 is rendered after the default queue geometry, and eventually the overlap is turned back to green. Visible, when Ztest is passed, the previous write color cache overwrites the previous one, which means that the final output is the last rendered object color.
Take a look at what the greater related parts do, this time we all use the default render state, and the Green cube shader ztest is set to greater:
This effect is more fun, although we found in the comparison of the depth, the front was shaded by the Blue cube part, the final green cover blue, is the desired result, but where the other parts? Simple analysis, the rendering order is the front-to-back, that is, blue first rendered, the default depth is Max, the depth of the blue cube satisfies the lequal condition, the depth of the cache is written, then the green to start rendering, the overlapping part of the depth of the cache is the blue cube write, While the depth of the green value is greater than the blue depth of the condition, so the depth test through, overlapping part of the color update to green, and the Red Cube coincident with the portion of the Red Cube final rendering, with the previous part of the depth test, less than the previous part, the depth test failed, the overlap is not updated to red, So the overlapping part is finally green. And the green cube does not overlap with other parts of the place why disappeared? In fact, because the Green cube rendering, in addition to the Blue cube rendering of the place is the depth of information, the other part of the depth of the information is Max, the blue part with greater to judge, will certainly fail, there will be no color updates.
Have a fun effect can actually test ztest greater to achieve, is the game often appear, when the player is obscured by other scene objects, the masked part will show the effect of x-ray; in fact, when rendering players, added a pass, the default pass normal rendering, An added pass uses greater for in-depth testing, so that when the player is obscured by other parts, the obscured portion is displayed, rendered with a stroke effect, and other parts still use the original pass.
Early-z Technology
In the traditional rendering pipeline, ztest is actually in the blending stage, when the depth test, all the object's pixel shader will be calculated again, no performance improvement, just to get the correct occlusion results, will cause a lot of useless calculation, because each pixel is bound to overlap a lot of calculations. As a result, the Early-z technology is used in modern GPUs to perform a deep test between the vertex and fragment phases (after rasterization, fragment), and if the depth test fails, the fragment phase calculation is not necessary. Therefore, there will be a great improvement in performance. However, the final ztest still need to be carried out to ensure that the final occlusion relationship results are correct. The previous one was mainly z-cull in order to be trimmed for optimization purposes, the latter was mainly z-check, in order to check, such as:
Early-z implementation, mainly through a z-pre-pass implementation, in short, for all opaque objects (transparent no use, itself will not write depth), first with a super simple shader to render, this shader not write color buffer, write depth buffer, The second pass closes the depth write, turns on the depth test, and renders with the normal shader. In fact, this technology, we can also learn, in rendering transparent objects, because the closed depth of writing, sometimes there will be other opaque parts of the transparent part of the block, and we actually do not want them to be obscured, only want to be obscured by the object half-transparent, then we can use two pass to render, The first pass uses a color mask to mask the colour write, write depth only, the second pass is rendered half-transparent, and the depth write is turned off.
For Early-z technology You can refer to ATI's paper applications of Explicit early-z culling and PPT, as well as an article from Intel.
Unity Rendering Order Summary
If we first draw the back object and then draw the object in front, it will cause over draw, and through the early-z technique, we can draw the closer object first, and then draw the farther object (only opaque object), so that by first rendering the front object, let the front object occupy the pit first, Can let the back of the object depth test failure, and thus reduce the repeated fragment calculation, to achieve the purpose of optimization. The default in unity should be to draw at the closest distance, and we can look at the official unity document that shows:
From the process shown in the document, this depth-test occurs between the vertex phase and the fragment phase, which is the Early-z optimization described above.
Simply summarize the rendering order in unity: render opaque objects first, in order from front to back, and then with transparent objects, in order from back to front.
Why Alpha Test (www.zbcppt.com Discard) is expensive on mobile platforms
From I just began to touch the rendering, began to hear the mobile platform Alpha test comparison fee, at that time more puzzled, directly discard why dues, should be more provincial only right ah? This question has been bothering me for a long time. or the Early-z optimization that we talked about above. Normally, for example, if we render a patch, whether it is an open depth write or a depth test, the depth value of the corresponding pixel after rasterization of the patch can be judged at the Early-z (z-cull) stage, and if Alpha Test (Discard) is turned on, Discard this operation is carried out in the fragment stage, that is, the patch after the corresponding pixel is visible, is in the fragment stage after the knowledge, the final need to rely on Z-check to determine the final color of the pixel. In fact, imagine can also know that if we open the Alpha test and also use early-z words, a piece should have been shaved place, it is still written into the depth of the cache, this will cause the other parts of a completely nothing to hide, the final rendering effect is definitely wrong. So, if we turn on Alpha test and do not postpone the early-z,z test to fragment, then the shader of the object will fully execute vertex shader and fragment shader, resulting in over Draw One way to do this is to use alpha blend instead of alpha test, although it is also very cost, but at least the Alpha blend does not write depth, but the depth test can be done in advance because it will not be visible at the fragment stage, because it is visible, Just a little more transparency. However, this is only a stopgap measure, and Alpha blend does not replace the Alpha Test completely.
Unity shader-render Queue, ztest,zwrite,early-z