Surface shader z order/transparency

I am trying to modify an existing, multi-pass shader into a single surface shader for batching/instancing capabilities. Unfortunately, the single pass version doesnt seem to render the mesh elements in the same order, causing some weird transparency issues.

Here is what the multi-pass shader renders (properly):


(notice the foliage at the bottom appears in front of the plant stems)

Here is the shader code:

Shader "2Sided_ColorMask" {
    Properties{
        _Color("Color", Color) = (1,1,1,1)
        _MainTex("Albedo (RGB)", 2D) = "white" {}
        _MaskTex("Color Mask", 2D) = "white"{}
        _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
    }

        SubShader{
            Tags { "Queue" = "Transparent" "RenderType" = "TransparentCutout" "IgnoreProjector" = "True" }

            Blend SrcAlpha OneMinusSrcAlpha

            LOD 200
            Cull Off
          
          
            // this is the vert/frag shader pass - this pass does not seem to batch properly but renders well
            Pass {

                    CGPROGRAM
                    #pragma vertex vert fullforwardshadows alpha
                    #pragma fragment frag fullforwardshadows alpha
                  
                    #include "UnityCG.cginc"
                    struct v2f {
                        float4 vertex : SV_POSITION;
                        float2 texcoord : TEXCOORD0;
                    };

                    sampler2D _MainTex;
                    sampler2D _MaskTex;
                    fixed _Cutoff;

                        v2f vert(appdata_img v)
                        {
                            v2f o;
                            o.vertex = UnityObjectToClipPos(v.vertex);
                            o.texcoord = v.texcoord;
                            return o;
                        }

                        fixed4 frag(v2f i) : SV_Target
                        {
                            clip(c.a - _Cutoff);

                            return c;
                        }

                    ENDCG
                }

      

        // surface shader that handles color masking

            CGPROGRAM

            #pragma surface surf Standard fullforwardshadows alpha:fade vertex:vert
            #pragma target 3.0

#include "UnityCG.cginc"
                        sampler2D _MainTex;
                        sampler2D _MaskTex;
                        float _Cutoff;                      

                        struct Input {
                            float2 uv_MainTex, uv_MaskTex;
                        };
                        struct v2f {
                            float4 vertex : SV_POSITION;
                            float2 texcoord : TEXCOORD0;
                        };


                        // Add instancing support for this shader.
                        UNITY_INSTANCING_CBUFFER_START(Props)
                            // put more per-instance properties here
                            UNITY_DEFINE_INSTANCED_PROP(fixed4, _Color)
                            UNITY_INSTANCING_CBUFFER_END

                            void vert(inout appdata_full v)
                            {
                                // blank since we're not using it in this test                           
}

// color masking surface function
                            void surf(Input IN, inout SurfaceOutputStandard o)
                            {

                                // Albedo comes from a texture tinted by color
                                float4 c = tex2D(_MainTex, IN.uv_MainTex);

                                float mask = tex2D(_MaskTex, IN.uv_MainTex).r;

                                c.rgb = c.rgb * (1 - mask) + UNITY_ACCESS_INSTANCED_PROP(_Color) * mask;
                                clip(c.a - _Cutoff);

                                o.Albedo = c.rgb;

                                o.Alpha = c.a;


                            }
                        ENDCG

        }
            FallBack "VertexLit"
}

If I only use the surface shader from above (i.e, exclude the Pass), it renders it like this:

The plant stems are always in front, regardless of camera angle.

Im not sure if this is just an OIT issue or something that cannot be addressed in a single surface shader but it seems like it should be straightforward to fix.

Any help would be greatly appreciated!

Thanks

1 Like

Yep.

Alpha blended objects that don’t write to depth will be drawn in the order of the mesh’s triangles. That bundle of stems are being drawn last so they’re on top.

I wrote about why this is an issue for real time rendering here:

Using an OIT approximation is an option, but they’re not always great at handling “opaque” transparent geometry and are mostly for making several layers of transparency not look obviously out of order.

Simple solutions would be to use basic alpha testing, ie: remove the alpha:fade line. You might also consider splitting up the mesh into separate parts with the leaves using a transparent material and the stems using an opaque material. If you plan on using forward rendering and MSAA you could use alpha to coverage.

1 Like

I will have to resort to basic alpha testing for this case. Thanks very much for the help and extended information!