Is it possible to fix the Transparency + Depth sorting issue?

Hello, I have two transparent characters against a transparent floor that the user can view at any distance/angle.
Thing is, when rotating or moving the camera, said objects overlap on each other. It is my understanding from this question that this is due the way the objects are sorted, based on their distance from the camera. Alas, I believe none of the solutions suggested would fix the issue in my case; would you happen to know a way to fix it or make the issue more bearable?

Here is an example of what I am talking about:
https://gfycat.com/MassivePepperyAppaloosa
Thank you!

You can order them manually by adjusting the queue in the material.

Edit: Ah, the issue is between the people. There are a few options.

  1. Only draw the geometry that is at the front. Can be done easily be first drawing z-only for all people and then drawing color only. Easiest and removes the visual popping.
  2. For multiple layers you can look at order independent transparency. Can be a lot trickier.

I apologize, I still struggle a lot understanding shaders. How could I first draw z-only? Would that be with a Tag within the subshader or a Blend type? My current shader is a humongous extension of the current Unity standard shader with lots of passes; I just kept on pasting code from the forums that seemed to do what I needed. It works but I am not sure if it is redundant.
Here it is just in case:

Shader "TransparentVertexColor" {

    Properties
    {
        _Color("Color", Color) = (1,1,1,1)
        _MainTex("Albedo", 2D) = "white" {}

        _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5

        _Glossiness("Smoothness", Range(0.0, 1.0)) = 0.5
        _GlossMapScale("Smoothness Scale", Range(0.0, 1.0)) = 1.0
        [Enum(Metallic Alpha,0,Albedo Alpha,1)] _SmoothnessTextureChannel("Smoothness texture channel", Float) = 0

        [Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0
        _MetallicGlossMap("Metallic", 2D) = "white" {}

        [ToggleOff] _SpecularHighlights("Specular Highlights", Float) = 1.0
        [ToggleOff] _GlossyReflections("Glossy Reflections", Float) = 1.0

        _BumpScale("Scale", Float) = 1.0
        _BumpMap("Normal Map", 2D) = "bump" {}

        _Parallax("Height Scale", Range(0.005, 0.08)) = 0.02
        _ParallaxMap("Height Map", 2D) = "black" {}

        _OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
        _OcclusionMap("Occlusion", 2D) = "white" {}

        _EmissionColor("Color", Color) = (0,0,0)
        _EmissionMap("Emission", 2D) = "white" {}

        _DetailMask("Detail Mask", 2D) = "white" {}

        _DetailAlbedoMap("Detail Albedo x2", 2D) = "grey" {}
        _DetailNormalMapScale("Scale", Float) = 1.0
        _DetailNormalMap("Normal Map", 2D) = "bump" {}

        [Enum(UV0,0,UV1,1)] _UVSec("UV Set for secondary textures", Float) = 0

        [HideInInspector] _Mode("__mode", Float) = 0.0
        [HideInInspector] _SrcBlend("__src", Float) = 1.0
        [HideInInspector] _DstBlend("__dst", Float) = 0.0
        [HideInInspector] _ZWrite("__zw", Float) = 1.0

        _IntensityVC("Vertex Color Intencity", Float) = 1.0
    }

    CGINCLUDE
        #define UNITY_SETUP_BRDF_INPUT MetallicSetup
    ENDCG

    SubShader
    {
        Tags{ "RenderType" = "Transparent" "PerformanceChecks" = "False" }
        LOD 30000

        Pass
        {
            ZWrite On
            ColorMask 0

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f {
            float4 pos : SV_POSITION;
        };

        v2f vert(appdata_base v)
        {
            v2f o;
            o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
            return o;
        }

        half4 frag(v2f i) : COLOR
        {
            return half4 (0,0,0,0);
        }
            ENDCG
        }

        Pass
        {
            Name "FORWARD"
            Tags{ "LightMode" = "ForwardBase" }

            Blend[_SrcBlend][_DstBlend]
            ZWrite On

            CGPROGRAM
            #pragma target 3.0

            #pragma shader_feature _NORMALMAP
            #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
            #pragma shader_feature _EMISSION
            #pragma shader_feature _METALLICGLOSSMAP
            #pragma shader_feature ___ _DETAIL_MULX2
            #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
            #pragma shader_feature _ _SPECULARHIGHLIGHTS_OFF
            #pragma shader_feature _ _GLOSSYREFLECTIONS_OFF
            #pragma shader_feature _PARALLAXMAP
            #pragma shader_feature _VERTEXCOLOR_OFF _VERTEXCOLOR _VERTEXCOLOR_LERP

            #pragma multi_compile_fwdbase
            #pragma multi_compile_fog

            #pragma vertex vertForwardBase_VC
            #pragma fragment fragForwardBase_VC

            #include "UnityStandardCore.cginc"
            #include "UnityVC.cginc"

            ENDCG
        }

        Pass
        {
            Name "FORWARD_DELTA"
            Tags{ "LightMode" = "ForwardAdd" }
            Blend[_SrcBlend] One
            Fog{ Color(0,0,0,0) }
            ZWrite Off
            ZTest LEqual

            CGPROGRAM
            #pragma target 3.0

            #pragma shader_feature _NORMALMAP
            #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
            #pragma shader_feature _METALLICGLOSSMAP
            #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
            #pragma shader_feature _ _SPECULARHIGHLIGHTS_OFF
            #pragma shader_feature ___ _DETAIL_MULX2
            #pragma shader_feature _PARALLAXMAP
            #pragma shader_feature _VERTEXCOLOR

            #pragma multi_compile_fwdadd_fullshadows
            #pragma multi_compile_fog

            #pragma vertex vertForwardAdd_VC
            #pragma fragment fragForwardAdd_VC

            #include "UnityStandardCore.cginc"
            #include "UnityVC.cginc"
            ENDCG
        }

        Pass
        {
            Name "ShadowCaster"
            Tags{ "LightMode" = "ShadowCaster" }

            ZWrite On ZTest LEqual

            CGPROGRAM
            #pragma target 3.0
           
            // -------------------------------------

            #pragma shader_feature _VERTEXCOLOR
            #pragma shader_feature _VERTEXCOLOR_LERP
            #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
            #pragma multi_compile_shadowcaster

            #pragma vertex vertShadowCaster_VC
            #pragma fragment fragShadowCaster_VC

            #include "UnityStandardShadow.cginc"
            #include "UnityVCShadow.cginc"

            ENDCG
        }

        Pass
        {
            Name "DEFERRED"
            Tags{ "LightMode" = "Deferred" }

            CGPROGRAM
            #pragma target 3.0
            #pragma exclude_renderers nomrt

            #pragma shader_feature _NORMALMAP
            #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
            #pragma shader_feature _EMISSION
            #pragma shader_feature _METALLICGLOSSMAP
            #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
            #pragma shader_feature _ _SPECULARHIGHLIGHTS_OFF
            #pragma shader_feature ___ _DETAIL_MULX2
            #pragma shader_feature _PARALLAXMAP
            #pragma shader_feature _VERTEXCOLOR_OFF _VERTEXCOLOR _VERTEXCOLOR_LERP

            #pragma multi_compile ___ UNITY_HDR_ON
            #pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON
            #pragma multi_compile ___ DIRLIGHTMAP_COMBINED DIRLIGHTMAP_SEPARATE
            #pragma multi_compile DYNAMICLIGHTMAP_OFF DYNAMICLIGHTMAP_ON

            #pragma vertex vertDeferred_VC
            #pragma fragment fragDeferred_VC

            #include "UnityStandardCore.cginc"
            #include "UnityVC.cginc"

            ENDCG
        }

        Pass
        {
            Name "META"
            Tags{ "LightMode" = "Meta" }

            Cull Off

            CGPROGRAM
            #pragma vertex vert_meta
            #pragma fragment frag_meta

            #pragma shader_feature _EMISSION
            #pragma shader_feature _METALLICGLOSSMAP
            #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
            #pragma shader_feature ___ _DETAIL_MULX2

            #include "UnityStandardMeta.cginc"
            ENDCG
        }
    }

    SubShader
    {
        Tags{ "RenderType" = "Opaque" "PerformanceChecks" = "False" }
        LOD 150

        Pass
        {
            Name "FORWARD"
            Tags{ "LightMode" = "ForwardBase" }

            Blend[_SrcBlend][_DstBlend]
            ZWrite[_ZWrite]

            CGPROGRAM
            #pragma target 2.0

            #pragma shader_feature _NORMALMAP
            #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
            #pragma shader_feature _EMISSION
            #pragma shader_feature _METALLICGLOSSMAP
            #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
            #pragma shader_feature _ _SPECULARHIGHLIGHTS_OFF
            #pragma shader_feature _ _GLOSSYREFLECTIONS_OFF
            #pragma shader_feature _VERTEXCOLOR_OFF _VERTEXCOLOR _VERTEXCOLOR_LERP


            #pragma skip_variants SHADOWS_SOFT DIRLIGHTMAP_COMBINED DIRLIGHTMAP_SEPARATE

            #pragma multi_compile_fwdbase
            #pragma multi_compile_fog

            #pragma vertex vertForwardBase_VC
            #pragma fragment fragForwardBase_VC

            #include "UnityStandardCore.cginc"
            #include "UnityVC.cginc"

            ENDCG
        }

        Pass
        {
            Name "FORWARD_DELTA"
            Tags{ "LightMode" = "ForwardAdd" }
            Blend[_SrcBlend] One
            Fog{ Color(0,0,0,0) }
            ZWrite Off
            ZTest LEqual

            CGPROGRAM
            #pragma target 2.0

            #pragma shader_feature _NORMALMAP
            #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
            #pragma shader_feature _METALLICGLOSSMAP
            #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
            #pragma shader_feature _ _SPECULARHIGHLIGHTS_OFF
            #pragma shader_feature ___ _DETAIL_MULX2

            #pragma skip_variants SHADOWS_SOFT
            #pragma shader_feature _VERTEXCOLOR

            #pragma multi_compile_fwdadd_fullshadows
            #pragma multi_compile_fog

            #pragma vertex vertForwardAdd_VC
            #pragma fragment fragForwardAdd_VC

            #include "UnityStandardCore.cginc"
            #include "UnityVC.cginc"
            ENDCG
        }

        Pass
        {
            Name "ShadowCaster"
            Tags{ "LightMode" = "ShadowCaster" }

            ZWrite On ZTest LEqual

            CGPROGRAM
            #pragma target 2.0

            #pragma shader_feature _VERTEXCOLOR
            #pragma shader_feature _VERTEXCOLOR_LERP
            #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
            #pragma skip_variants SHADOWS_SOFT
            #pragma multi_compile_shadowcaster

            #pragma vertex vertShadowCaster_VC
            #pragma fragment fragShadowCaster_VC

            #include "UnityStandardShadow.cginc"
            #include "UnityVCShadow.cginc"

            ENDCG
        }

        Pass
        {
            Name "META"
            Tags{ "LightMode" = "Meta" }

            Cull Off

            CGPROGRAM
            #pragma vertex vert_meta
            #pragma fragment frag_meta

            #pragma shader_feature _EMISSION
            #pragma shader_feature _METALLICGLOSSMAP
            #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
            #pragma shader_feature ___ _DETAIL_MULX2

            #include "UnityStandardMeta.cginc"
            ENDCG
        }
    }

    FallBack "VertexLit"
    CustomEditor "StandardShaderVCGUI"
}

Thank you!

This part would be z-only:

ZWrite On
ColorMask 0

So that’s the first pass. The second pass would just have ZWrite Off, because it’s no longer needed.

Order Independent Transparency (OIT) is the search term you’re looking for.

To do accurate per pixel transparency sorting is extremely expensive to do on modern graphics cards, though it is possible. To do it in Unity would require essentially not using any of Unity’s built in shaders, and possible not use any of Unity’s built in renderer components. Pretty much no one does this outside of some tech demos and specific applications, these days it’s probably cheaper to “just” do GPU based raytracing … but that’s even more work to implement.

There are approximated OIT techniques that have gained a lot of popularity. They aren’t perfectly accurate, but they’re usually good enough that with out knowing they weren’t accurate you wouldn’t be able to tell the difference. Weighted Blended Transparency is one that a few people have released free basic implementations of, like here:

Another option is to abuse alpha to coverage / alpha to mask and MSAA, but it requires more than just turning it on (which is fairly easy, just make a new shader and add AlphaToMask On) as you need to manually control the mask bits so overlaps show. It also make transparency aliasing worse as it works by abusing MSAA samples.

The “Pre-Z” option @jvo3dc is suggesting can solve the issue, but it’ll also make your objects not look transparent as you won’t be able to see them through each other.

The easiest solution would be just use a blend mode that does’t require sorting, like additive.

5 Likes

Here’s an example of the alpha to coverage hack. You must have 4x MSAA anti-aliasing turned on for this to work. Only two objects will overlap, after that it’s fully opaque, but both objects will individually be ~80% opaque unless they overlap. For your example above you’ll need each character to be using materials with a different “Object” setting, but leave the ground using the material it currently has.

Shader "Unlit/CoverageMask4XMSAA" {
Properties {
    _MainTex ("Base (RGB)", 2D) = "white" {}
    [Enum(A, 14, B, 13, C, 11, D, 7)] _Coverage ("Coverage Object", Int) = 14
}

SubShader {
    Tags { "RenderType"="Opaque" }
    LOD 100
   
    Pass {
        AlphaToMask On

        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 5.0
            #pragma multi_compile_fog
           
            #include "UnityCG.cginc"

            struct appdata_t {
                float4 vertex : POSITION;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f {
                float4 vertex : SV_POSITION;
                float2 texcoord : TEXCOORD0;
                UNITY_FOG_COORDS(1)
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
           
            v2f vert (appdata_t v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            uint _Coverage;
           
            fixed4 frag (v2f i, out uint mask : SV_Coverage) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.texcoord);
                UNITY_APPLY_FOG(i.fogCoord, col);
                UNITY_OPAQUE_ALPHA(col.a);
                mask = _Coverage;
                return col;
            }
        ENDCG
    }
}

}
3 Likes

Thank you so much for your responses!

I tried both implementations, the “ZWrite On” during the first pass and the alpha coverage hack (Really interesting stuff!). The thing is that I also need to display the vertices color which, along the alpha values, vary constantly each frame and I wasn’t able to integrate this into your solutions :frowning:

Nevertheless, @bgolus , the link that you provided in your first post mentioned this GitHub project that seems rather frail and hacky but gives great results! Somehow I was able to add vertex color without breaking it.

Also, thank you for dropping the “OIT” term too, I learned a lot from googling it!

2 Likes