• 中文
  • multi-dim-material order in layers error with depth writing

Related Discussions
...

i wondering to enable the depth writing at same time when it's rendering
so i added the "Depth" pass into Spine-Skeleton.shader
it works when the mesh render always working with a single material
BUT it doesn't work right when the mesh render working with multi-materials
material rendering orders were randomly changing, and i don't know why,it's confusing me.

Shader "Spine/Skeleton" {
   Properties {
      _Cutoff ("Alpha cutoff", Range(0,1)) = 0.01
      _Color ("Color", Color) = (1.000000,1.000000,1.000000,1.000000)
      _Cutoff ("Shadow alpha cutoff", Range(0,1)) = 0.1
      _GrayLerp ("GrayLerp",Range(0,1)) = 1
      [NoScaleOffset] _MainTex ("Main Texture", 2D) = "black" {}
      [Toggle(_STRAIGHT_ALPHA_INPUT)] _StraightAlphaInput("Straight Alpha Texture", Int) = 0
      [HideInInspector] _StencilRef("Stencil Reference", Float) = 1.0
      [HideInInspector][Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp("Stencil Comparison", Float) = 8 // Set to Always as default

  // Outline properties are drawn via custom editor.
  [HideInInspector] _OutlineWidth("Outline Width", Range(0,8)) = 3.0
  [HideInInspector] _OutlineColor("Outline Color", Color) = (1,1,0,1)
  [HideInInspector] _OutlineReferenceTexWidth("Reference Texture Width", Int) = 1024
  [HideInInspector] _ThresholdEnd("Outline Threshold", Range(0,1)) = 0.25
  [HideInInspector] _OutlineSmoothness("Outline Smoothness", Range(0,1)) = 1.0
  [HideInInspector][MaterialToggle(_USE8NEIGHBOURHOOD_ON)] _Use8Neighbourhood("Sample 8 Neighbours", Float) = 1
  [HideInInspector] _OutlineMipLevel("Outline Mip Level", Range(0,3)) = 0
   }

   SubShader {
      Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" }

  Fog { Mode Off }
  Cull Off
  ZWrite Off
  Lighting Off
  

  
  Blend One OneMinusSrcAlpha
  
  Stencil {
     Ref[_StencilRef]
     Comp[_StencilComp]
     Pass Keep
  }

  Pass {
     Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" }
     Name "Normal"
     ColorMask RGBA
     CGPROGRAM
     #pragma shader_feature _ _STRAIGHT_ALPHA_INPUT
     #pragma vertex vert
     #pragma fragment frag
     #include "UnityCG.cginc"
     sampler2D _MainTex;
     fixed _Cutoff;
     float _GrayLerp;
     float4 _Color;
        
     struct VertexInput {
        float4 vertex : POSITION;
        float2 uv : TEXCOORD0;
        float4 vertexColor : COLOR;
     };

     struct VertexOutput {
        float4 pos : SV_POSITION;
        float2 uv : TEXCOORD0;
        float4 vertexColor : COLOR;
     };

     VertexOutput vert (VertexInput v) {
        VertexOutput o;
        o.pos = UnityObjectToClipPos(v.vertex);
        o.uv = v.uv;
        o.vertexColor = v.vertexColor;
        return o;
     }

     float4 frag (VertexOutput i) : SV_Target {
        float4 texColor = tex2D(_MainTex, i.uv);

        #if defined(_STRAIGHT_ALPHA_INPUT)
        texColor.rgb *= texColor.a;
        #endif
        float4 allColor = texColor * i.vertexColor * _Color;
        fixed gray = 0.30 * allColor.r + 0.59 * allColor.g + 0.11 * allColor.b;
        fixed3 finalColor = lerp(gray, allColor, _GrayLerp);

        return fixed4(finalColor.r, finalColor.g, finalColor.b,allColor.a);
     }
     ENDCG
  }
  
  Pass {
     Tags { "LightMode"="Vertex" "Queue"="Transparent" "IgnoreProjector"="true" "RenderType"="Transparent" }
     
     Name "Depth"
     ColorMask 0
     ZWrite On
     
     CGPROGRAM
     #pragma target 2.0
     #pragma shader_feature _ _STRAIGHT_ALPHA_INPUT
     #pragma vertex vert
     #pragma fragment frag
     #define _ALPHA_CLIP
     #include "UnityCG.cginc"
     sampler2D _MainTex;
     fixed _Cutoff;
     
     struct VertexInput {
        float4 vertex : POSITION;
        float2 uv : TEXCOORD0;
        float4 vertexColor : COLOR;
     };

     struct VertexOutput {
        float4 pos : SV_POSITION;
        float2 uv : TEXCOORD0;
        float4 vertexColor : COLOR;
     };

     VertexOutput vert (VertexInput v) {
        VertexOutput o;
        o.pos = UnityObjectToClipPos(v.vertex);
        o.uv = v.uv;
        o.vertexColor = v.vertexColor;
        return o;
     }

     float4 frag (VertexOutput i) : SV_Target {
        float4 texColor = tex2D(_MainTex, i.uv);

        #if defined(_STRAIGHT_ALPHA_INPUT)
        texColor.rgb *= texColor.a;
        #endif
        texColor = texColor * i.vertexColor;
        clip(texColor.a - _Cutoff);
        clip(-1.0);
        return texColor;
     }
     ENDCG
  }

  Pass {
     Name "Caster"
     Tags { "LightMode"="ShadowCaster" }
     Offset 1, 1
     ZWrite On
     ZTest LEqual

     Fog { Mode Off }
     Cull Off
     Lighting Off

     CGPROGRAM
     #pragma vertex vert
     #pragma fragment frag
     #pragma multi_compile_shadowcaster
     #pragma fragmentoption ARB_precision_hint_fastest
     #include "UnityCG.cginc"
     sampler2D _MainTex;
     fixed _Cutoff;

     struct VertexOutput {
        V2F_SHADOW_CASTER;
        float4 uvAndAlpha : TEXCOORD1;
     };

     VertexOutput vert (appdata_base v, float4 vertexColor : COLOR) {
        VertexOutput o;
        o.uvAndAlpha = v.texcoord;
        o.uvAndAlpha.a = vertexColor.a;
        TRANSFER_SHADOW_CASTER(o)
        return o;
     }

     float4 frag (VertexOutput i) : SV_Target {
        fixed4 texcol = tex2D(_MainTex, i.uvAndAlpha.xy);
        clip(texcol.a * i.uvAndAlpha.a - _Cutoff);
        SHADOW_CASTER_FRAGMENT(i)
     }
     ENDCG
  }
   }
   CustomEditor "SpineShaderWithOutlineGUI"
}

could you please tell me how to solve it,thanks!

Most likely you don't need a separate Depth pass, instead you can just change ZWrite Off to ZWrite On. Then you need to set Advanced - Z-Spacing at your SkeletonRenderer component, otherwise you will receive z-fighting problems.

In general it is advisable to copy any thirdparty shader and then modify the copied version instead of modifying the original, because e.g. when updating the spine-unity runtime you might accidentally overwrite your changes. In the case of the shader, you should then also change the name of the shader in code from Spine/Skeleton to e.g. Spine/Skeleton ZWrite so that you can select it separately in the Material's Shader property.

Thanks for your advice
i just got a wrong rendering if i just change ZWrite off to ZWrite on - some slots with sub-materials were DISAPPEAR when rendering
and i don't get the 'Advanced Z-Spacing' what it is exactly
its range seems from 0 to -1 in the inspector
i NEED set the Z as the world-space Z


It still doesn't work with a separate Z-Write pass or just turn ZWrite On
some slots with sub-materials are random changes in the order of the layers
e.g some times material A in top of the layers,and some times material B in top of the layers

NinjaDog wrote

and i don't get the 'Advanced Z-Spacing' what it is exactly

Please always have a look at the spine-unity documentation pages first if anything is unclear:
spine-unity Runtime Documentation: Setting Advanced Parameters

NinjaDog wrote

i just got a wrong rendering if i just change ZWrite off to ZWrite on - some slots with sub-materials were DISAPPEAR when rendering

It could be that you need to have ZWrite enabled on all your submaterials, not only on some of them. Unfortunately we don't know enough about your setup without any screenshots provided. In general please always provide some screenshots (of the scene view and of the active Materials at your SkeletonRenderer) that show the issues you are having, then we can help much more efficiently.

there are 8 same spines on the screen,and some quads with textures for background
it's the correct rendering BUT without the zwrite,the beam of lighting and mountain are really a spine same as other characters,just playing a different animation

with a separated z writing,keep eye on the beam of lighting,it's behind the mountain,it's wrong

with the 'ZWrite On',it's completely wrong,mountains are DISAPPEAR


let's say the Spine named as A
if there is just one 'A',it works with separated Zwriting
but it doesn't works if there are 8(multiple) 'A'
weird

NinjaDog wrote

let's say the Spine named as A
if there is just one 'A',it works with separated Zwriting
but it doesn't works if there are 8(multiple) 'A'
weird

If it works correctly with only one instance, but the order is getting wrong with multiple instances, it's most likely due to too aggressive batching behaviour by Unity, grouping things incorrectly (e.g. batching all of your _anan materials to one draw call and all _anan2 materials to another, messing with the layer order). Please see this forum thread here for solutions (most likely a SortingGroup component should help):
multiple characters on scene issue

We also cover this in the documentation page:
spine-unity Runtime Documentation: Transparency and Draw Order

In general it is advisable to use as few switches between atlas pages as possible, and group your attachments accordingly (or even better, make sure they fit on a single atlas texture). In your case you have 8 draw calls per skeleton, which could potentially be reduced to 3 via proper grouping, as described here on the documentation pages:
spine-unity Runtime Documentation: Material Switching and Draw Calls

Cool,i've been use

Render.sortingOrder

instead of SortingGroup before.
Every instance have correct layer orders with 'Fix Draw Order' right now.
Thanks for you advice!

Glad to hear, thanks for getting back to us!

5 months later

Hi,We found same issues with some specified spine files,not same one as previous.and 5% of our spine files has same issues.
I tried to upgrade to Spine 4.0 to see if it is fixed,but we found two problem:
1) Some spine animation is displayed in WRONG position/scale in 4.0 and they both fine in 3.8(Unity),so i've post it in this thread:
Spine animation displayed WRONG in Unity Spine 4.0
2) With 5% spine files still has wrong orders in layers with 'Fixed Draw Order',I found it can be correctly if the 'Visible Outside Mask' is enabled in Spine Unity 4.0 - i noticed the fps is slow down if i turned 'VisibleOutMask' on. BUT i think it is still a bug unfixed.


AND Sorry for my describe was wrong as "Order in layers error with depth writing",it actually independent to depth writing.
It occurred in original unity spine 3.8/4.0 package with specified spine files.


and I will provide a test case (included specified spine files)as soon as possible , for help you to figure it out


I can't post the attachment because '413 Request Entity Too Large' (It's 19MB)

The test case has been sent to your email,thanks a lot!

Thanks for sending the reproduction assets, we just had a look at it. You are not using the SortingGroup component that we suggested to use some postings above. If I add a SortingGroup at each SkeletonAnimation GameObject in your provided scene, everything is rendered in the correct order. You can also disable Advanced - Fix Draw Order which is intended for use with Universal Render Pipeline.

This is under the assumption that you are using the normal render pipeline and not Universal Render Pipeline (URP) in your project.

Sorry,I forgot to provide the script about the SortingOrder

In practice, our scene would add an automatic sorting component to set the order for each SkeletonAnimation
and this component automatically computes the order based on Z,
so gameObjects of the same Z will be under the same order value

So this still exists in our scene
Is that means can't use the same value of order even if they're in the same z-plane, right?


and yes,we're using normal render pipeline,not URP

NinjaDog wrote

Is that means can't use the same value of order even if they're in the same z-plane, right?

No. I just added a SortingGroup component to each SkeletonAnimation GameObject and left it at the default values (Order in Layer set to 0). This solved the problem already.

Thanks a lot. but sounds it works different to my project.
I will try it again and back.
What Unity version I use is 2018.4.14.f1.
AND my script is using the 'Order' of 'Render' component,to avoid an extra 'SortingOrder',for make it as simple as I can.

NinjaDog wrote

AND my script is using the 'Order' of 'Render' component,to avoid an extra 'SortingOrder'

I assume you mean SortingGroup instead of SortingOrder component, sortingOrder instead of Order and Renderer instead of Render component.

That will not work (unless you assign different sortingOrder values at every skeleton MeshRenderer), you need to prevent Unity's batching mechanisms from re-grouping (reordering) triangles across two equal skeletons at the same depth.

NinjaDog wrote

..for make it as simple as I can.

I could imagine even simpler solutions that don't work 😛.

Joking aside: solving the reordering problem by setting a slightly different Renderer.sortingOrder parameter at each Skeleton's MeshRenderer could work as well. However, I would call this solution more complicated and not very clean, given that the SortingGroup component was designed for exactly this purpose. See the official SortingGroup documentation here.

I'm really appreciate for your explain and help!

Thanks for your kind words, glad that it helped!

Yes,this problem has been resolved in Unity Spine 3.8!