Shaders breaking on specific android devices [Urgent, over live 30k players have the issue]

Yesterday I released a new update to my playerbase, test went well, until I released to a 100% of players
This is the bug in question: Imgur: The magic of the Internet
The bug appears to happen to around 2-3% of players, which adds up to around 30k players.

The issue seems to be device specific, and seems to mostly happen to older phones.

Shaders with issues:
Part shader:

Shader "Sprites/Part"
{
    Properties
    {
        _TextureAtlas("Texture Atlas", 2D) = "white" {}
        _Intensity("Shade Intensity", float) = 0
    }

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

        Cull Off
        Lighting Off
        Blend One OneMinusSrcAlpha
   
        Pass {
        CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile _ ETC1_EXTERNAL_ALPHA
#include "UnityCG.cginc"

    struct appdata_t
    {
        float4 vertex : POSITION;
        fixed4 color : COLOR;
        float3 UVQ_0 : TEXCOORD0;
        float3 UVQ_1 : TEXCOORD1;
        float3 UVQ_2 : TEXCOORD2;
        float3 UVQ_3 : TEXCOORD3;

        UNITY_VERTEX_INPUT_INSTANCE_ID
    };

    struct v2f
    {
        float4 vertex : SV_POSITION;
        fixed4 color : COLOR;
        float3 UVQ_0 : TEXCOORD0;
        float3 UVQ_1 : TEXCOORD1;
        float3 UVQ_2 : TEXCOORD2;
        float3 UVQ_3 : TEXCOORD3;

        UNITY_VERTEX_OUTPUT_STEREO
    };

    v2f vert(appdata_t IN)
    {
        v2f OUT;

        UNITY_SETUP_INSTANCE_ID(IN);
        UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);

        OUT.vertex = UnityObjectToClipPos(IN.vertex);

        OUT.UVQ_0 = IN.UVQ_0;
        OUT.UVQ_1 = IN.UVQ_1;
        OUT.UVQ_2 = IN.UVQ_2;
        OUT.UVQ_3 = IN.UVQ_3;
        OUT.color = IN.color;

        return OUT;
    }

    sampler2D _TextureAtlas;
    float _Intensity;

    fixed4 frag(v2f IN, out float depth : SV_Depth) : SV_Target
    {
        // Color texture // Vertice color
        fixed4 c = tex2D(_TextureAtlas, IN.UVQ_0.xy / IN.UVQ_0.z) * IN.color;

        // Shape texture // Shadow texture
        float albedo = tex2D(_TextureAtlas, IN.UVQ_1.xy / IN.UVQ_1.z).r * 1.85 * (1 - (1 - tex2D(_TextureAtlas, IN.UVQ_2.xy / IN.UVQ_2.z).r) * _Intensity);

        // Alpha / Albedo
        c.rgb *= albedo * c.a;

        // Depth

#if defined(UNITY_REVERSED_Z)
        depth = IN.UVQ_3.x;
#else
        depth = 1.0f - IN.UVQ_3.x;
#endif

        return c;
    }
        ENDCG
    }
    }
}

Terrain shader:

Shader "SFS/Terrain"
{
    Properties
    {
        _PlanetTexture ("Planet Texture", 2D) = "white" {}
        _PlanetTextureCutout ("Planet Texture Cutout", Float) = 1

        _TextureA ("Texture A", 2D) = "white" {}
        _TextureB ("Texture B", 2D) = "white" {}
        _TextureTerrain ("Texture Terrain", 2D) = "white" {}

        _RepeatA ("Repeat A", Vector) = (1,1,1,1)
        _RepeatB ("Repeat B", Vector) = (1,1,1,1)
        _RepeatTerrain ("Repeat Terrain", Vector) = (1,1,1,1)

        _SurfaceSize ("Texture Transition", Float) = 1

        _Min ("Min fade", Float) = 0   
        _Max ("Max fade", Float) = 1

        _ShadowSize ("Transition shade", Float) = 1
        _ShadowIntensity ("Shade intensity", Float) = 1   
   
        _Fog ("Atmosphere Color", Color) = (1,1,1,1)

        _Depth("Depth",  float) = 0
    }

    SubShader {

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

        Cull Off
        Lighting Off
        ZWrite Off
        Blend Off

        Pass {
        CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile _ ETC1_EXTERNAL_ALPHA

#include "UnityCG.cginc"

    // Data passed into the vert function
    struct appdata_t
    {
        float4 vertex : POSITION;
        float2 uv : TEXCOORD0;
        float2 uv2 : TEXCOORD1;
        float2 uv3 : TEXCOORD2;
    };

    // Data passed into the frag function
    struct v2f
    {
        float4 vertex : SV_POSITION;
        float2 uv : TEXCOORD0;
        float2 uv2 : TEXCOORD1;
        float2 uv3 : TEXCOORD2;
    };

    float _ShadowIntensity;

    v2f vert(appdata_t IN){

        v2f OUT;
       
        OUT.vertex = UnityObjectToClipPos( IN.vertex);

        OUT.uv = IN.uv;
        OUT.uv2 = IN.uv2;

        OUT.uv3 = float2( IN.uv3.x * _ShadowIntensity, IN.uv3.y);

        return OUT;
    }

    sampler2D _PlanetTexture;
    float _PlanetTextureCutout;

    sampler2D _TextureA;
    float4 _RepeatA;
    sampler2D _TextureB;
    float4 _RepeatB;
    sampler2D _TextureTerrain;
    float4 _RepeatTerrain;

    float _SurfaceSize;

    float _Max;
    float _Min;
   
    float _ShadowSize;

    float4 _Fog;

    float _Depth;

    fixed4 frag(v2f IN, out float depth : SV_Depth) : SV_TARGET
    {
        depth = _Depth;

        IN.uv = float2(IN.uv.x / (1 - IN.uv.y), IN.uv.y);

        float lerpAmount = clamp( IN.uv.y * _SurfaceSize, _Min, _Max);

        float grayscale =
        (
        (tex2D( _TextureA, IN.uv * _RepeatA.xy).r * (1 - IN.uv3.y) + tex2D( _TextureB, IN.uv * _RepeatB.xy).r * (IN.uv3.y) ) * (1 - lerpAmount) // Surface texture
        +
        tex2D( _TextureTerrain, IN.uv * _RepeatTerrain.xy ).r * (0.95 * lerpAmount) // Terrain texture
        )
        *
        2
        *
        (1 + IN.uv3.x * (1 - min( 1, IN.uv.y * _ShadowSize)) ); // Shade

        float4 f4 = (tex2D( _PlanetTexture, IN.uv2 * _PlanetTextureCutout + 0.5) * grayscale) * (1 - _Fog.a) + (_Fog * _Fog.a);

        f4.a = 1;

        return f4;
    }
        ENDCG
    }
    }
}

Sprite shader:

Shader "SFS/Sprite"
{
    Properties
    {
        [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
        _Color("Tint", Color) = (1,1,1,1)
        _Depth("Depth",  float) = 0
        [MaterialToggle] PixelSnap("Pixel snap", Float) = 0
        [HideInInspector] _RendererColor("RendererColor", Color) = (1,1,1,1)
        [HideInInspector] _Flip("Flip", Vector) = (1,1,1,1)
        [PerRendererData] _AlphaTex("External Alpha", 2D) = "white" {}
        [PerRendererData] _EnableExternalAlpha("Enable External Alpha", Float) = 0
    }

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

        Cull Off
        Lighting Off
        ZWrite Off
        Blend One OneMinusSrcAlpha

        Pass
        {
        CGPROGRAM
            #pragma vertex SpriteVert
            #pragma fragment SpriteFrag
            #pragma target 2.0
            #pragma multi_compile_instancing
            #pragma multi_compile  PIXELSNAP_ON
            #pragma multi_compile  ETC1_EXTERNAL_ALPHA
            #include "Depth.cginc"
        ENDCG
        }
    }
}

With a lot of messing around, we managed to get the terrain shader working by removing ā€œout depthā€, however that did not seem to work for the part shader.

Any help would be really appreciated, i’m getting absolutely spammed with reports of this bug.

Hi!
If you make a development build on a device where this fails, it will output what problem does it encounter in the logcat.
This just means that some shaders are not supported on this device.
Is there anything common between the devices where those shaders don’t work?

We found this error:
-------- GLSL link error: L0010 Uniform ā€˜_WorldSpaceLightPos0’ differ on precision

Any idea what it could be or how could we solve it?

Full log

OdinSerializer detected Android architecture 'armv7l' for determining unaligned read/write capabilities. Unaligned read/write support: all=False, float=True => Log
UnityEngine.Logger:Log(LogType, Object)
Sirenix.Serialization.ArchitectureInfo:SetIsOnAndroid(String)
Sirenix.Serialization.UnitySerializationInitializer:Initialize()

UnityIAP: Unity Ads not present, Promotional placements not possible => Log
UnityEngine.Purchasing.StandardPurchasingModule:Instance(AppStore)
ŪŚ„Ó’Ųŗ:޻أ݀ڕ()

Calling android JNI method => Log
UnityEngine.Logger:Log(LogType, Object)
CodeStage.AntiCheat.Genuine.CodeHash.AndroidWorker:Execute()
CodeStage.AntiCheat.Genuine.CodeHash.CodeHashGenerator:GenerateInternal()

[ACTk] Obscured Cheating Detector: was started without any callbacks. Please configure Detection Event in the inspector, or pass the callback Action to the StartDetection method. => Warning
UnityEngine.Debug:LogWarning(Object, Object)
CodeStage.AntiCheat.Detectors.ObscuredCheatingDetector:StartDetectionInternal(Action)

-------- GLSL link error: L0010 Uniform '_WorldSpaceLightPos0' differ on precision

=> Error

UnityIAP: Unity Ads not present, Promotional placements not possible => Log
UnityEngine.Purchasing.Extension.UnityUtil:Update()

UnityIAP: Unity Ads not present, Promotional placements not possible => Log
UnityEngine.Purchasing.Extension.UnityUtil:Update()

UnityIAP: Unity Ads not present, Promotional placements not possible => Log
UnityEngine.Purchasing.Extension.UnityUtil:Update()

KeyNotFoundException: The given key was not present in the dictionary. => Exception
System.Collections.Generic.Dictionary`2[TKey,TValue].get_Item (TKey key) (at <00000000000000000000000000000000>:0)
SFS.Parts.PartTexture.Get_UV (SFS.Parts.Modules.Pipe shape, Line segment, System.Single shapeWidth, UnityEngine.Transform meshHolder, UnityEngine.Vector2 lightDirection) (at <00000000000000000000000000000000>:0)
SFS.Parts.Modules.Textures.GetOutput (SFS.Parts.Modules.Pipe shape, UnityEngine.Transform meshHolder, UnityEngine.Vector2 lightDirection) (at <00000000000000000000000000000000>:0)
SFS.Parts.Modules.PipePartMesh.Get_UV_Channels (SFS.Parts.Modules.Pipe shape) (at <00000000000000000000000000000000>:0)
SFS.Parts.Modules.PipeMesh.GenerateMesh () (at <00000000000000000000000000000000>:0)
SFS.Variables.OnChange.Invoke () (at <00000000000000000000000000000000>:0)
SFS.Variables.Composed`1[T].op_Addition (SFS.Variables.Composed`1[T] a, SFS.Variables.OnChange b) (at <00000000000000000000000000000000>:0)
SFS.Parts.Modules.PipePartMesh.I_Setup.Setup () (at <00000000000000000000000000000000>:0)
SFS.Parts.Part.InitializePart () (at <00000000000000000000000000000000>:0)
SFS.Parts.PartsLoader.CreatePart (SFS.Parts.Modules.VariantRef variant) (at <00000000000000000000000000000000>:0)
SFS.Builds.PickGrid.CreateParts (SFS.Parts.Modules.VariantRef[] parts) (at <00000000000000000000000000000000>:0)
SFS.Builds.PickGrid.SetPicklist (SFS.Builds.PickGrid+CollectedPicklist A) (at <00000000000000000000000000000000>:0)
SFS.Builds.PickGrid.Setup () (at <00000000000000000000000000000000>:0)
SFS.Variables.OnChange.Invoke () (at <00000000000000000000000000000000>:0)
SFS.Variables.LocalVariable`1[T].op_Addition (SFS.Variables.LocalVariable`1[T] a, SFS.Variables.OnChange b) (at <00000000000000000000000000000000>:0)
SFS.Builds.PickGrid.Start () (at <00000000000000000000000000000000>:0)

UnityIAP: Unity Ads not present, Promotional placements not possible => Log
UnityEngine.Purchasing.Extension.UnityUtil:Update()

KeyNotFoundException: The given key was not present in the dictionary. => Exception
System.Collections.Generic.Dictionary`2[TKey,TValue].get_Item (TKey key) (at <00000000000000000000000000000000>:0)
SFS.Parts.PartTexture.Get_UV (SFS.Parts.Modules.Pipe shape, Line segment, System.Single shapeWidth, UnityEngine.Transform meshHolder, UnityEngine.Vector2 lightDirection) (at <00000000000000000000000000000000>:0)
SFS.Parts.Modules.Textures.GetOutput (SFS.Parts.Modules.Pipe shape, UnityEngine.Transform meshHolder, UnityEngine.Vector2 lightDirection) (at <00000000000000000000000000000000>:0)
SFS.Parts.Modules.PipePartMesh.Get_UV_Channels (SFS.Parts.Modules.Pipe shape) (at <00000000000000000000000000000000>:0)
SFS.Parts.Modules.PipeMesh.GenerateMesh () (at <00000000000000000000000000000000>:0)
SFS.Variables.OnChange.Invoke () (at <00000000000000000000000000000000>:0)
SFS.Variables.Composed`1[T].op_Addition (SFS.Variables.Composed`1[T] a, SFS.Variables.OnChange b) (at <00000000000000000000000000000000>:0)
SFS.Parts.Modules.PipePartMesh.I_Setup.Setup () (at <00000000000000000000000000000000>:0)
SFS.Parts.Part.InitializePart () (at <00000000000000000000000000000000>:0)
SFS.Parts.PartsLoader.CreatePart (SFS.Parts.Modules.VariantRef variant) (at <00000000000000000000000000000000>:0)
SFS.Builds.PickGrid.CreateParts (SFS.Parts.Modules.VariantRef[] parts) (at <00000000000000000000000000000000>:0)
SFS.Builds.PickGrid.SetPicklist (SFS.Builds.PickGrid+CollectedPicklist A) (at <00000000000000000000000000000000>:0)
SFS.Builds.PickGrid.Setup () (at <00000000000000000000000000000000>:0)
SFS.Variables.OnChange.Invoke () (at <00000000000000000000000000000000>:0)
SFS.Variables.LocalVariable`1[T].op_Addition (SFS.Variables.LocalVariable`1[T] a, SFS.Variables.OnChange b) (at <00000000000000000000000000000000>:0)
SFS.Builds.PickGrid.Start () (at <00000000000000000000000000000000>:0)

KeyNotFoundException: The given key was not present in the dictionary. => Exception
System.Collections.Generic.Dictionary`2[TKey,TValue].get_Item (TKey key) (at <00000000000000000000000000000000>:0)
SFS.Parts.PartTexture.Get_UV (SFS.Parts.Modules.Pipe shape, Line segment, System.Single shapeWidth, UnityEngine.Transform meshHolder, UnityEngine.Vector2 lightDirection) (at <00000000000000000000000000000000>:0)
SFS.Parts.Modules.Textures.GetOutput (SFS.Parts.Modules.Pipe shape, UnityEngine.Transform meshHolder, UnityEngine.Vector2 lightDirection) (at <00000000000000000000000000000000>:0)
SFS.Parts.Modules.PipePartMesh.Get_UV_Channels (SFS.Parts.Modules.Pipe shape) (at <00000000000000000000000000000000>:0)
SFS.Parts.Modules.PipeMesh.GenerateMesh () (at <00000000000000000000000000000000>:0)
SFS.Variables.OnChange.Invoke () (at <00000000000000000000000000000000>:0)
SFS.Variables.Composed`1[T].op_Addition (SFS.Variables.Composed`1[T] a, SFS.Variables.OnChange b) (at <00000000000000000000000000000000>:0)
SFS.Parts.Modules.PipePartMesh.I_Setup.Setup () (at <00000000000000000000000000000000>:0)
SFS.Parts.Part.InitializePart () (at <00000000000000000000000000000000>:0)
SFS.Parts.PartsLoader.CreatePart (SFS.Parts.Modules.VariantRef variant) (at <00000000000000000000000000000000>:0)
SFS.Builds.HoldGrid.SelectParts (SFS.Input.TouchPosition touchPosition) (at <00000000000000000000000000000000>:0)
SFS.Builds.BuildManager.OnTouchStart (SFS.Input.TouchStartData data) (at <00000000000000000000000000000000>:0)
SingleDelegate`1[T].Invoke (T value) (at <00000000000000000000000000000000>:0)
SFS.Input.TouchInput.TouchStart (System.Int32 fingerId, SFS.Input.TouchPosition touchPosition) (at <00000000000000000000000000000000>:0)
SFS.Input.TouchInput.GetTouchInputs () (at <00000000000000000000000000000000>:0)

KeyNotFoundException: The given key was not present in the dictionary. => Exception
System.Collections.Generic.Dictionary`2[TKey,TValue].get_Item (TKey key) (at <00000000000000000000000000000000>:0)
SFS.Parts.PartTexture.Get_UV (SFS.Parts.Modules.Pipe shape, Line segment, System.Single shapeWidth, UnityEngine.Transform meshHolder, UnityEngine.Vector2 lightDirection) (at <00000000000000000000000000000000>:0)
SFS.Parts.Modules.Textures.GetOutput (SFS.Parts.Modules.Pipe shape, UnityEngine.Transform meshHolder, UnityEngine.Vector2 lightDirection) (at <00000000000000000000000000000000>:0)
SFS.Parts.Modules.PipePartMesh.Get_UV_Channels (SFS.Parts.Modules.Pipe shape) (at <00000000000000000000000000000000>:0)
SFS.Parts.Modules.PipeMesh.GenerateMesh () (at <00000000000000000000000000000000>:0)
SFS.Variables.OnChange.Invoke () (at <00000000000000000000000000000000>:0)
SFS.Variables.Composed`1[T].op_Addition (SFS.Variables.Composed`1[T] a, SFS.Variables.OnChange b) (at <00000000000000000000000000000000>:0)
SFS.Parts.Modules.PipePartMesh.I_Setup.Setup () (at <00000000000000000000000000000000>:0)
SFS.Parts.Part.InitializePart () (at <00000000000000000000000000000000>:0)
SFS.Parts.PartsLoader.CreatePart (SFS.Parts.Modules.VariantRef variant) (at <00000000000000000000000000000000>:0)
SFS.Builds.HoldGrid.SelectParts (SFS.Input.TouchPosition touchPosition) (at <00000000000000000000000000000000>:0)
SFS.Builds.BuildManager.OnTouchStart (SFS.Input.TouchStartData data) (at <00000000000000000000000000000000>:0)
SingleDelegate`1[T].Invoke (T value) (at <00000000000000000000000000000000>:0)
SFS.Input.TouchInput.TouchStart (System.Int32 fingerId, SFS.Input.TouchPosition touchPosition) (at <00000000000000000000000000000000>:0)
SFS.Input.TouchInput.GetTouchInputs () (at <00000000000000000000000000000000>:0)

KeyNotFoundException: The given key was not present in the dictionary. => Exception
System.Collections.Generic.Dictionary`2[TKey,TValue].get_Item (TKey key) (at <00000000000000000000000000000000>:0)
SFS.Parts.PartTexture.Get_UV (SFS.Parts.Modules.Pipe shape, Line segment, System.Single shapeWidth, UnityEngine.Transform meshHolder, UnityEngine.Vector2 lightDirection) (at <00000000000000000000000000000000>:0)
SFS.Parts.Modules.Textures.GetOutput (SFS.Parts.Modules.Pipe shape, UnityEngine.Transform meshHolder, UnityEngine.Vector2 lightDirection) (at <00000000000000000000000000000000>:0)
SFS.Parts.Modules.PipePartMesh.Get_UV_Channels (SFS.Parts.Modules.Pipe shape) (at <00000000000000000000000000000000>:0)
SFS.Parts.Modules.PipeMesh.GenerateMesh () (at <00000000000000000000000000000000>:0)
SFS.Variables.OnChange.Invoke () (at <00000000000000000000000000000000>:0)
SFS.Variables.Composed`1[T].op_Addition (SFS.Variables.Composed`1[T] a, SFS.Variables.OnChange b) (at <00000000000000000000000000000000>:0)
SFS.Parts.Modules.PipePartMesh.I_Setup.Setup () (at <00000000000000000000000000000000>:0)
SFS.Parts.Part.InitializePart () (at <00000000000000000000000000000000>:0)
SFS.Parts.PartsLoader.CreatePart (SFS.Parts.Modules.VariantRef variant) (at <00000000000000000000000000000000>:0)
SFS.Builds.HoldGrid.SelectParts (SFS.Input.TouchPosition touchPosition) (at <00000000000000000000000000000000>:0)
SFS.Builds.BuildManager.OnTouchStart (SFS.Input.TouchStartData data) (at <00000000000000000000000000000000>:0)
SingleDelegate`1[T].Invoke (T value) (at <00000000000000000000000000000000>:0)
SFS.Input.TouchInput.TouchStart (System.Int32 fingerId, SFS.Input.TouchPosition touchPosition) (at <00000000000000000000000000000000>:0)
SFS.Input.TouchInput.GetTouchInputs () (at <00000000000000000000000000000000>:0)

I heavily doubt the KeyNotFoundException causes the pink shaders, as they also happen to other completely unrelated parts of the game

No, this link error is the culprit.
I also suppose this happens only on OpenGL ES2 devices, is that right?
Can you please report this as a bug?
Also, I don’t see your shaders using _WorldSpaceLightPos0… Is it in some .cginc file?
I might be able to provide a workaround if I see the code that uses it.

Yes it only happens on OpenGL ES2.

We figure out its this part of the code:
fixed4 frag(v2f i, out float depth : SV_Depth)
If we stop using depth, the shader works (That also unfortunately breaks our game render order)

Currently i’m creating a copy of every shader using depth, asking the users manually if their game is pink, and applying the alternative shaders. Their render order is still broken, but better than a pink screen.

I would be really happy to know if there is any workaround.

OpenGL ES 2 does not support writing to depth from shaders. Only devices that have GL_EXT_frag_depth extension support https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_frag_depth.txt can do this.
Why do you need writing depth for rendering? This turns off some optimizations and makes rendering slower.

I found this fix, for anyone that has this problem in the future:

#if defined(SHADER_API_GLES)
    fixed4 frag(v2f IN) : SV_Target
    {
        // Color texture // Vertice color
        fixed4 c = tex2D(_TextureAtlas, IN.UVQ_0.xy / IN.UVQ_0.z) * IN.color;

        // Alpha / Albedo
        c.rgb *= albedo * c.a;

        return c;
    }
#else
        fixed4 frag(v2f IN, out float depth : SV_Depth) : SV_Target
    {
        // Color texture // Vertice color
        fixed4 c = tex2D(_TextureAtlas, IN.UVQ_0.xy / IN.UVQ_0.z) * IN.color;

        // Alpha
        c.rgb *= albedo * c.a;

        // Depth
        #if defined(UNITY_REVERSED_Z)
        depth = IN.UVQ_3.x;
        #else
        depth = 1.0f - IN.UVQ_3.x;
        #endif

        return c;
    }
#endif
1 Like

Our game uses 2.5d rendering. It was the only way we could make things like this work: 5951957--637748--upload_2020-6-8_10-0-0.png
We tried everything, even making them actual 3d models etc
(Unity does some weird optimization for 3d models where entire model has the same render order)

If you have any creative idea, we would gladly get rid of this entire depth system.

I’m not entirely sure what you mean by ā€œthings like thisā€ :slight_smile:

Might be a better example:
5952203--637793--upload_2020-6-8_11-37-5.png

Why can’t you work with separate 3D models here?

We are not a 100% sure why, but it just did not work.
Our theory is that for optimization, each rendered mesh has a render order calculated from its distance to camera.

Either way, #if defined(SHADER_API_GLES) worked
The backup shader does not have the fancy depth system, but the game is still playable.

Render order is calculated based on camera sorting flags: Unity - Scripting API: Camera.opaqueSortMode and Unity - Scripting API: Camera.transparencySortMode.

Opaque 3D models don’t actually care what their render order is in terms of the final image. The whole reason why the depth buffer exists is so that the final image is deterministic regardless of draw order.

However, if you swapped to using 3D models, but were still using transparent materials / shaders, those generally do not write to the depth buffer, and the draw order matters again, just like any other transparent sprite.

Yes we have transparent textures, the whole system has to be able to handle them.