Health Bar "Reveal" shader with 1 drawcall, 1 texture

Hello,

After playing with surface shaders, I found a way to make a reveal type shader for Health bars in any UI.

The way it works is simple : It multiplies the image alpha with a static mask value for the whole texture, hiding it progressively from left to right and from bottom to top, all controlled with a slider.

The slider range is UV values, from 0 to 1, so all you have to do is at runtime to specify the said sprite UVs as this value (or it would be unprecise, as 1 = whole texture is masked, 0 = none).
The shader also take into account the vertex color.

The advantage of this technique over AlphaTesting a Mask texture is obviously performance for iOS / PowerVR hardware. As mentionned numerous times in this forum and in Unity docs, AlphaTest for iOS is to be avoided at all costs (some old thread).

Shader "MaskedSprite" {
Properties {
	_MainTex ("Texture", 2D) = "white" {}
 	_CutoffX("CutoffX", Range(0.0,1.0)) = 1
 	_CutoffY("CutoffY", Range(0.0,1.0)) = 1
}


	
	Category {
	
	
		
		
	
		Cull off
		Blend SrcAlpha OneMinusSrcAlpha
		Lighting Off

			

Fog { Mode Off }

	
	 
	SubShader {
		
		Tags {"Queue"="Transparent" "RenderType"="Transparent"}
		

	CGPROGRAM
      #pragma surface surf Unlit alpha

	 half4 LightingUnlit (SurfaceOutput s, half3 lightDir, half atten) {

          half4 c;
          c.rgb = s.Albedo;
          c.a = s.Alpha;
          return c;
      }
      
		sampler2D _MainTex;
      	
       struct Input {
       	float2 uv_MainTex;
         float4 color : Color;
     	 };
     
	fixed _CutoffX;
	fixed _CutoffY;

      void surf (Input IN, inout SurfaceOutput o) {
      
	half4 tex = tex2D (_MainTex, IN.uv_MainTex);
		
	o.Albedo = IN.color.rgb*tex.rgb;
        o.Alpha = IN.uv_MainTex.x > _CutoffX ? 0 : IN.uv_MainTex.y > _CutoffY ? 0 : IN.color.a*tex.a;
      	
      }


      
      ENDCG
		
		
}

}

}

I bolded the important part (so you could copy paste it in your own shader).
The mask is based on a rectangular hiding, indeed.

Example : if I want to mask my health bar from right to left, I would have to put it this shader, and at runtime perform (pseudo code to gain time, but you get the idea) :

HealthBar.material.SetFloat(“_CutoffX”, HealthBar_Mesh_UV_TopRight-myValue);

If you want the hide to operate on the left side instead of right, you juste invert the comparison signs in the o.Alpha operation.

Hope it helps.

Awesome Work! After spending hours and hours of research, this was exactly what i was looking for!

This is a great shader thanks!

However I’m trying to get it to work on a plane which has a tiled texture. Is there a way to apply this shader across the whole tiled plane instead of the first texture tile only?

Sorry for the (very) late reply (I didn’t sub to the thread, my bad) … I guess you figured it out now, but the answer I’m guessing is no, as it’s working on full object, so if you’re tiling several objects, it will apply the shader once per object. Maybe I’m wrong though, as I didn’t try it.

Anyway, this shader was made back when there were no GUI solution proposing such trick, but now there are plenty. I suggest that for health bar, you use builtin mask management from popular GUI solutions (ex2D, 2D Toolkit, NGUI, etc). Putting the work on a shader will give an extra drawcall for each object, which is not optimal at all :slight_smile:

This is still an extra draw call, but a relatively simple shader. It uses the main texture’s alpha channel as a ramp for revealing the RGB. You can use a simple linear gradient for a linear reveal, or put some noise in it for a dissolve effect. Of course, you can also use whatever shape you want for the reveal (radial, conical, etc.) and it should look pretty nice. The lerp avoids alpha testing and conditionals, and also results in a smooth transition rather than a hard edge.

Shader "Custom/Reveal" {
	Properties {
		_MainTex ("Base (RGB) Reveal (A)", 2D) = "white" {}
		_Cutoff ("Cutoff", Range(-0.5, 0.55)) = 0
	}
	SubShader {
		Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
		LOD 100
		Alphatest Greater 0
		ZWrite Off
		Blend SrcAlpha OneMinusSrcAlpha 
		ColorMask RGB
		
		Pass {
			
			CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#include "UnityCG.cginc"
				
				struct v2f {
					float4 pos : SV_POSITION;
					float2 uv_MainTex : TEXCOORD0;
				};
				
				float4 _MainTex_ST;
				
				v2f vert(appdata_base v) {
					v2f o;
					o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
					o.uv_MainTex = TRANSFORM_TEX(v.texcoord, _MainTex);
					return o;
				}
				
				sampler2D _MainTex;
				fixed _Cutoff;
				
				float4 frag(v2f IN) : COLOR {
					half4 c = tex2D (_MainTex, IN.uv_MainTex);
					c.a = lerp(-20, 20, c.a + _Cutoff);
					return c;
				}
			ENDCG
		}
	}
}

1009943--37403--$Bilinear dissolve.png

I’m quite lost about “runtime to specify the said sprite UVs as this value” :confused:

LOL, sorry for that, after reading again that part I realize it’s awfully written xD
May I suggest you to use Daniel’s solution, which is far more elegant :slight_smile:

how to reveal part of img with click of mouse?