(2D/sprites) How to render fragments with highest alpha on top of other fragments

Hi,

I am trying to render a 2D character that has 6 different parts(head/body/arms/legs/sword/shield). All parts are separate sprites but they all have the same dimensions and pivot point.

7875124--1001203--head.png 7875124--1001200--body.png 7875124--1001197--arms.png 7875124--1001206--legs.png
They look like this overall:

I also have a depth texture for each sprite, in same dimensions as main sprites. I will convert them to alpha value of the main sprites for performance reasons later.(Please look at the first comment for depth textures, I cant add more files)

For all parts of the character I want only the fragment with the highest alpha value(per pixel) to render, but I don’t know how to achieve that. Any help is appreciated.

Some notes:

  • The character will have flipbook animation, so the main textures will keep changing
  • Sprite sorting wont cut it because one limb can be above and below another limb at the same time.
  • There will be other characters in the scene with a similar setup, so I need the fragment sorting done per character if that makes sense
  • I am a complete beginner with hlsl
  • Something like this could possibly work, but I don’t know how it would work with flipbook sprites. Just using rgb values from the highest alpha value per character part is the easy part

Aforementioned depth textures:
7875127--1001221--head.depth.png 7875127--1001224--body.depth.png 7875127--1001227--arms.depth.png 7875127--1001230--legs.depth.png

Currently darker fragments are closer and should render after white fragments but this is just for visualizing the example

To do per-pixel depth sorting you need to render your sprites using a shader that is opaque and uses alpha testing instead of alpha blending. Then you would need to have your sprites output their per pixel depth from the fragment shader. If you want multiple characters to sort separately and not intersect you either need to actually keep them separated in Z space.

Alternatively you would need to render each character into a render texture separately and then use the resulting render texture when rendering them into the scene.

The post you linked to which blends multiple textures together is only possible when all of the textures are being rendered by a single mesh. The fact your body parts are separate sprites excludes this as an option. Unless you use something like the aforementioned render texture approach and render each individual sprite to a render texture and then combine those.

check Using 2D sprites and depth textures to fake pixel depth.

Thanks for the help, I managed to achieve the effect I was looking for.


interestingly enough order in layer doesn’t work for the custom shader but I don’t mind it, I have decided to use it on just the player anyway.

Toggling ZWrite doesn’t seem to change anything but it still works.

here is the full shader code if anyone is interested. Let me know if you have any comments/tips about the shader code.(PS: I’ll combine the main & depth textures to reduce sampling and size later)

Shader "Custom/2D"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        [PerRendererData] _DepthTex ("Depth Texture", 2D) = "white" {}
    }

    SubShader
    {
        Tags
        {
            "Queue"="Geometry" //setting to Transparent doesn't seem to change anything
            "RenderType"="Opaque" //setting to Transparent doesn't seem to change anything
        }

        ZWrite On//enabling or disabling doesn't seem to change anything
        //ZTest LEqual
        Blend SrcAlpha OneMinusSrcAlpha//necessary for removing 'artifacts' also need to set alpha is transparency to false in depth texture

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

            sampler2D _MainTex;
            sampler2D _DepthTex;
         
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 uv  : TEXCOORD0;
            };

            struct fragmentOutput
            {
                fixed4 color:SV_Target;
                float depth:SV_Depth;
            };
         
            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos (v.vertex);
                o.uv = v.uv;

                return o;
            }

            fragmentOutput frag(v2f i)
            {
                fragmentOutput output;

                output.color= tex2D(_DepthTex, i.uv);
                output.depth=tex2D(_DepthTex, i.uv);

                return output;
            }
            ENDCG
        }
    }
}