Search Unity

Shader: How to write a shader that respects sorting order first and then depth

Discussion in 'Shaders' started by unity_ee5j2CmTTucraQ, Sep 16, 2021.

  1. unity_ee5j2CmTTucraQ

    unity_ee5j2CmTTucraQ

    Joined:
    Mar 9, 2021
    Posts:
    1
    I am very new to shaders. I have created a custom editor for MeshRenderer in order for me to edit their sorting order (m_SortingOrder). I want to modify Unity's built-in mobile unlit shader (https://github.com/TwoTailsGames/Un...rcesExtra/Mobile/Mobile-Lightmap-Unlit.shader) to respect the sorting order first and then depth second. If I have objA with sorting order of 100 and objB with sorting order of 0, the objA should be drawn on top of objB, regardless of the depth. If they have the same sorting order, then the drawing order should be determined by the depth. However, it seems like the shader is disregarding the sorting order right now.

    Any help would be appreciated.
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    If you have a shader that does depth writes and depth tests ... the sorting order will not and cannot be respected. That's because the depth buffers explicit purpose was to make the sorting order not affect how the final image appears.

    The work around for this is you have to also positionally offset your meshes if you want a specific sorting order. If you want opaque 3D mesh A to always appear behind opaque 3D mesh B, then you need to actually separate the two so that A is actually behind B.

    The other hack would be to clear the depth buffer between "layers" that you don't want interacting. This can be done with multiple cameras, or by using a shader that just writes to the depth buffer.

    Code (csharp):
    1. Shader "Depth Clear"
    2. {
    3.     SubShader {
    4.         Pass {
    5.             ColorMask 0 // only render to depth
    6.             Cull Off
    7.             ZWrite On
    8.             ZTest Always
    9.  
    10.             CGPROGRAM
    11.             #pragma vertex vert
    12.             #pragma fragment frag
    13.  
    14.             #include "UnityCG.cginc"
    15.  
    16.             float4 vert (float4 vertex : POSITION) : SV_POSITION
    17.             {
    18.                 // expects you're using the default Unity quad
    19.                 float4 pos = float4(vertex.xy * 2.0, 0.0, 1.0);
    20.  
    21.                 // max depth is 1.0 for OpenGL, 0.0 for everything else
    22.                 // but if z == max depth it might get clipped, so set it just slightly inside the depth range
    23.             #if UNITY_REVERSED_Z
    24.                 pos.z = 0.000001;
    25.             #else
    26.                 pos.z = 0.999999;
    27.             #endif
    28.                 return pos;
    29.             }
    30.  
    31.             // the output of the frag doesn't matter since ColorMask 0 explicitly ignores anything it outputs
    32.             // but Unity still requires we have one, so make it as simple as possible
    33.             fixed4 frag() : SV_Target { return 0; }
    34.  
    35.             ENDCG
    36.         }
    37.     }
    38. }
    Slap that on a quad mesh, make sure the game object is visible to the camera so it doesn't get culled, and it'll clear depth buffer. You can then control when it renders using the material queue and sortingOrder to clear the depth after rendering 3D meshes you want to sort against each other..
     
    unity_ee5j2CmTTucraQ likes this.