Shader works in Editor/Windows but not on Android

This shader adds a post processor effect, on Windows/Editor it works, but on Android it does not appear. Just nothing, everything works, but the effect does not appear.

Shader "Custom/Highlight" {
Properties {
	_Color ("Color", Color) = (1,1,1,1)
	_MainTex ("Main Texture", 2D) = "black" {}
	_OccludeMap ("Occlusion Map", 2D) = "black" {}
}

SubShader {

	ZTest Always Cull Off ZWrite Off Fog { Mode Off }
	
	
	// OVERLAY GLOW
	
	Pass {
		CGPROGRAM
			#pragma vertex vert_img
			#pragma fragment frag
			#pragma fragmentoption ARB_precision_hint_fastest
			#include "UnityCG.cginc"
		
			sampler2D _MainTex;
			sampler2D _OccludeMap;
			
			fixed4 _Color;
		
			fixed4 frag(v2f_img IN) : COLOR 
			{
				fixed3 overCol = tex2D(_OccludeMap, IN.uv).r * _Color.rgb * _Color.a;
				return tex2D (_MainTex, IN.uv) + fixed4(overCol, 1.0);
			}
		ENDCG
	}
	
	// OVERLAY SOLID
	
	Pass {
		CGPROGRAM
			#pragma vertex vert_img
			#pragma fragment frag
			#pragma fragmentoption ARB_precision_hint_fastest
			#include "UnityCG.cginc"
		
			sampler2D _MainTex;
			sampler2D _OccludeMap;
			
			fixed4 _Color;
		
			fixed4 frag(v2f_img IN) : COLOR 
			{
				fixed4 mCol = tex2D (_MainTex, IN.uv);
				fixed oCol = tex2D (_OccludeMap, IN.uv).r;
				
				fixed solid = step (1.0 - _Color.a, oCol);
				return mCol + solid * fixed4(_Color.rgb, 1.0);
			}
		ENDCG
	}

	
	// OCCLUSION
	
	Pass {
			CGPROGRAM
			#pragma vertex vert_img
			#pragma fragment frag
			#pragma fragmentoption ARB_precision_hint_fastest
			#include "UnityCG.cginc"
		
			sampler2D _MainTex;
			sampler2D _OccludeMap;
		
			fixed frag(v2f_img IN) : COLOR 
			{
				return tex2D (_MainTex, IN.uv).r - tex2D(_OccludeMap, IN.uv).r;
			}
		ENDCG
	}
	
	Pass {
    
       	Tags {"RenderType"="Opaque"}
    	ZWrite On
    	ZTest LEqual
    	Fog { Mode Off }
    
        CGPROGRAM

        #pragma vertex vert
        #pragma fragment frag

        float4 vert(float4 v:POSITION) : POSITION {
            return mul (UNITY_MATRIX_MVP, v);
        }

        fixed frag() : COLOR {
            return 1.0;
        }

        ENDCG
    }	
	
    Pass {        	
       	Tags {"Queue"="Transparent"}
        Cull Back
        Lighting Off
        ZWrite Off
        ZTest LEqual
        ColorMask RGBA
        Blend OneMinusDstColor One

    
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"
        
        sampler2D _CameraDepthTexture;
        
        struct v2f {
            float4 vertex : POSITION;
            float4 projPos : TEXCOORD1;
        };
 
        v2f vert( float4 v : POSITION ) {        
            v2f o;
            o.vertex = mul( UNITY_MATRIX_MVP, v );
            o.projPos = ComputeScreenPos(o.vertex);             
            return o;
        }

        fixed frag( v2f i ) : COLOR {          
            float depthVal = LinearEyeDepth (tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)).r);
            float zPos = i.projPos.z;
            
            return step( zPos, depthVal );         
        }
        ENDCG
    }
} 
}

This works perfectly for a Windows build/Editor, but NOT in on an Android device/Emulator; it works via Unity Remote 4.

C# Script that uses it:

using UnityEngine;
using System.Collections.Generic;

using UnityStandardAssets.ImageEffects;
using UnityEngine.Rendering;

[RequireComponent(typeof(Camera))]
public class HighlightsPostEffect : MonoBehaviour 
{
#region enums
public enum HighlightType
{
	Glow = 0,
	Solid = 1
}
public enum SortingType
{
	Overlay = 3,
	DepthFilter = 4
}
public enum FillType
{
	Fill,
	Outline
}
public enum RTResolution
{
	Quarter = 4,
	Half = 2,
	Full = 1
}
#endregion

#region public vars

public HighlightType m_selectionType = HighlightType.Glow;
public SortingType m_sortingType = SortingType.DepthFilter;	
public FillType m_fillType = FillType.Outline;
public RTResolution m_resolution = RTResolution.Full;

public string m_occludeesTag = "Occludee";
public string m_occludersTag = "Occluder";
public Color m_highlightColor = new Color(1f, 0f, 0f, 0.65f);

public Shader m_highlightShader;

#endregion

#region private field

private BlurOptimized m_blur;

private Renderer[] highlightObjects;
private Renderer[] m_occluders = null;

private Material m_highlightMaterial;

private CommandBuffer m_renderBuffer;

private int m_RTWidth = 512;
private int m_RTHeight = 512;

#endregion

private void Awake()
{
	CreateBuffers();
	CreateMaterials();
	SetOccluderObjects();

    //m_blur = gameObject.AddComponent<BlurOptimized>();
    m_blur = gameObject.GetComponent<BlurOptimized>();
	m_blur.enabled = false;

	GameObject[] occludees = GameObject.FindGameObjectsWithTag(m_occludeesTag);
	highlightObjects = new Renderer[occludees.Length];

	for( int i = 0; i < occludees.Length; i++ )
		highlightObjects _= occludees*.GetComponent<Renderer>();*_

* m_RTWidth = (int) (Screen.width / (float) m_resolution);
m_RTHeight = (int) (Screen.height / (float) m_resolution);
_
}_
_
private void CreateBuffers()_
_
{_
m_renderBuffer = new CommandBuffer();
_
}_
_
private void ClearCommandBuffers()_
_
{_
m_renderBuffer.Clear();
_
}*_

* private void CreateMaterials()*
* {*
* m_highlightMaterial = new Material( m_highlightShader );
_
}_
_
private void SetOccluderObjects()_
_
{_
if( string.IsNullOrEmpty(m_occludersTag) )
_
return;*_

* GameObject occluderGOs = GameObject.FindGameObjectsWithTag(m_occludersTag);*

* List occluders = new List();*
* foreach( GameObject go in occluderGOs )*
* {*
* Renderer renderer = go.GetComponent();*
* if( renderer != null )*
* occluders.Add( renderer );*
* }*

* m_occluders = occluders.ToArray();
_
}*_

* private void RenderHighlights( RenderTexture rt)*
* {*
* if( highlightObjects == null )*
* return;*
* RenderTargetIdentifier rtid = new RenderTargetIdentifier(rt);*
* m_renderBuffer.SetRenderTarget( rtid );
_
for(int i = 0; i < highlightObjects.Length; i++)_
_
{_
_ if( highlightObjects == null )
continue;*_

m_renderBuffer.DrawRenderer( highlightObjects*, m_highlightMaterial, 0, (int) m_sortingType );
_ }
RenderTexture.active = rt;_

Graphics.ExecuteCommandBuffer(m_renderBuffer);
_ RenderTexture.active = null;
}*_

* private void RenderOccluders( RenderTexture rt)*
* {*
* if( m_occluders == null )
_ return;
RenderTargetIdentifier rtid = new RenderTargetIdentifier(rt);_

m_renderBuffer.SetRenderTarget( rtid );
m_renderBuffer.Clear();*

* foreach(Renderer renderer in m_occluders)
_ { _
m_renderBuffer.DrawRenderer( renderer, m_highlightMaterial, 0, (int) m_sortingType );
_ }
RenderTexture.active = rt;_

Graphics.ExecuteCommandBuffer(m_renderBuffer);
_ RenderTexture.active = null;
}
/// Final image composing.
/// 1. Renders all the highlight objects either with Overlay shader or DepthFilter*

* /// 2. Downsamples and blurs the result image using standard BlurOptimized image effect*
* /// 3. Renders occluders to the same render texture*
* /// 4. Substracts the occlusion map from the blurred image, leaving the highlight area*
* /// 5. Renders the result image over the main camera’s G-Buffer*
* private void OnRenderImage( RenderTexture source, RenderTexture destination )
{
RenderTexture highlightRT;_

RenderTexture.active = highlightRT = RenderTexture.GetTemporary(m_RTWidth, m_RTHeight, 0, RenderTextureFormat.R8 );
_ GL.Clear(true, true, Color.clear);
RenderTexture.active = null;
ClearCommandBuffers();
RenderHighlights(highlightRT);_

RenderTexture blurred = RenderTexture.GetTemporary( m_RTWidth, m_RTHeight, 0, RenderTextureFormat.R8 );
m_blur.OnRenderImage( highlightRT, blurred );*

* RenderOccluders(highlightRT);*
* if( m_fillType == FillType.Outline )
_ {_
RenderTexture occluded = RenderTexture.GetTemporary( m_RTWidth, m_RTHeight, 0, RenderTextureFormat.R8);
_ // Excluding the original image from the blurred image, leaving out the areal alone*
* m_highlightMaterial.SetTexture("OccludeMap", highlightRT);
Graphics.Blit( blurred, occluded, m_highlightMaterial, 2 );
m_highlightMaterial.SetTexture("OccludeMap", occluded);*
* RenderTexture.ReleaseTemporary(occluded);*

* }
else*

* {_
m_highlightMaterial.SetTexture("OccludeMap", blurred);*
* }_
m_highlightMaterial.SetColor("Color", m_highlightColor);
Graphics.Blit (source, destination, m_highlightMaterial, (int) m_selectionType);*

* RenderTexture.ReleaseTemporary(blurred);
RenderTexture.ReleaseTemporary(highlightRT);
}
public void RefreshHighlight()
{
//CreateBuffers();
//CreateMaterials();
SetOccluderObjects();_

//m_blur = gameObject.AddComponent();
//m_blur.enabled = false;
GameObject[] occludees = GameObject.FindGameObjectsWithTag(m_occludeesTag);
highlightObjects = new Renderer[occludees.Length];
for (int i = 0; i < occludees.Length; i++)
highlightObjects _= occludees.GetComponent();
m_RTWidth = (int)(Screen.width / (float)m_resolution);
m_RTHeight = (int)(Screen.height / (float)m_resolution);
}
}*_

Well, you’re trying to do post processing on mobile hardware, what did you expect? :smiley:

Seriously though, technical question: Are you using deferred rendering on your main camera?

If so, no wonder. Deferred rendering requires MRT (multiple render targets) tech, basically it means that a pixel shader can write to several render targets at once, instead of just one. This feature is not supported on mobile hardware yet, to the best of my knowledge. Therefore you lose your depth buffer and whatever else is required for your effects to work.

Putting that aside, any kind of post process on mobile will be horrible for performance because mobile devices have insane resolutions, but very low pixel fillrates - usually not enough to render out to the entire screen several times. Even reducing the resolution doesn’t help much since simply switching texture targets required for post process will be insanely expensive.

If I may be so bold as to ask, what is the nature of your application? Any particular reason why you can’t just stick with desktop platform?