Merge sprites by color

I have a series of sprites all on the same z-axis. I’d like to be able to merge their central color and not have their outline overlap, and be able to set its central color and outline. Just like the example below:

1176492--45162--$shader_example.png

I’ve figured out how to assign the colors, but am unsure how to merge the sprites images. I think I could set the z-depth or use a grabpass, but those are expensive on mobile devices, right?

Any tips?

Can you make the outlines and central parts separate sprites, and composite them as (in this case) four layers?

It looks like you’re trying to make a metaball shader. I’m not familiar with how it’d be implemented in ShaderLab though. I know that a long-time ago there used to be a screen-space ShaderLab tutorial for it though.

That is definitely one solution, Daniel. Have a separate outline sprite sitting below the base sprite. That’s a lot of overdraw though, and this feels like it can be done with a shader. And it’s a good exercise for me to learn more about shaders.

if all spirtes have the same zdepth ,I think a bit z offset -1,-1 of the central color will do your work

Yes that’s exactly what I’m thinking of! But I’ve pored over tons of shaders and forum posts and still cannot figure out how to manipulate the zdepth. Can you point me in the right direction?

I don’t think you can change depth values from a fragment shader on mobile. The deferred renderers used by all the hardware would have a really hard time with that even if they could do it, and doubling your overdraw would definitely be a faster solution.

Despite performance,you can do that with two passes,one with z offset,one without,and do alpha test in the correcct pass,then blend them

Again, though: deferred GPU architectures mean that an extra layer of overdraw is preferable to alpha testing.

Can anyone provide me a simple example of using a texture to set the z offset? It may very well be the wrong direction, but it’s something I’m interested in seeing and testing.

And thoughts on using a grab pass and manually blending each pixel?

Daniel means,the best solution on mobile is to instaciate two Objects, one above another,that will make your problem not a shader issue

Ok then that will likely be my final solution. Thanks all for the discussion! But I’d love to see an example of using a texture to set a z-offset. I’ve been trying to figure that out for days and hate to leave a problem unfinished…

http://forum.unity3d.com/threads/66153-Writing-depth-value-in-fragment-program

Also, if you decide to solve it with a GrabPass somehow, remember that that’s another layer of overdraw anyway.

two passes,with the second one has a smaller scale and z offset -1,-1,

even resovle the z_fight, From your picture,i have another question:How about the outline merge? you make it use postrender?2D Sprit is different with 3d model.And if you use mesh to make the circle,then use PostRender make the outline,I think you get it easier.

Ah ha! I did not understand that you could not manipulate z-depth on a pixel-by-pixel basis. It’s done for all the geometry in the pass. I’ve made a shader that does what I want. I still may use two sprites, but still wanted to try this and share. Thanks everyone for the hints!

If anyone is interested:
1179243--45421--$shader_example2.png

Shader "Assign Colors Z"
{
	Properties
	{
		_ColorMain	 ("Main Color", Color) = (0.5, 0.5, 0.5, 1.0)
		_ColorBorder ("Border", Color) = (1.0, 1.0, 1.0, 1.0)
		_MainTex	 ("Texture to blend", 2D) = "" {}
	}
	
	SubShader
	{
		Tags
		{
			"Queue" = "Transparent"
		}
		
		
		// Pass 1 draws all the circles with alpha.  They will overlap.
		Pass
		{
			Zwrite Off
			Blend SrcAlpha OneMinusSrcAlpha

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"
			
			uniform sampler2D _MainTex;
			half4 _ColorMain;
			half4 _ColorBorder;
			
			struct v2f
			{
				float4 pos : SV_POSITION;
				float2 uv  : TEXCOORD0;
			};
			
			
			v2f vert (appdata_base v)
			{
				v2f o;
				o.pos = mul( UNITY_MATRIX_MVP, v.vertex );
				o.uv  = v.texcoord.xy;
				return o;
			}
			
			
			// Convert black to the main color, and white to the border color.
			half4 frag (v2f i) : COLOR
			{
				// get the texture's color for this pixel
				half4 color = tex2D( _MainTex, i.uv );
				
				// get how far apart the border and main color are
				half4 delta = _ColorBorder - _ColorMain;
				
				// the final color is the percentage difference between the two.  more white = more border color.  less white = less border color.
				half4 main = _ColorMain + (color * delta);
				
				// manually set the alpha
				main.a = color.a;
				
				return main;
			}
			
			ENDCG
		}
		
		
		// Pass 2 redraws just the inner circle and pulls it towards the camera.  White texture pixels become alpha.
		Pass
		{
			Offset -1, -1
			
			Blend SrcAlpha OneMinusSrcAlpha
			
			// ditch any pixels with >50% alpha
			AlphaTest Greater 0.5
			
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"
			
			uniform sampler2D _MainTex;
			half4 _ColorMain;
			half4 _ColorBorder;
			
			struct v2f
			{
				float4 pos : SV_POSITION;
				float2 uv  : TEXCOORD0;
			};
			
			v2f vert (appdata_base v)
			{
				v2f o;
				o.pos = mul( UNITY_MATRIX_MVP, v.vertex );
				o.uv  = v.texcoord.xy;
				return o;
			}
			
			half4 frag (v2f i) : COLOR
			{
				// get the texture's color for this pixel
				half4 color = tex2D( _MainTex, i.uv );
				
				// set the main color for everything drawn
				half4 main = _ColorMain;
				
				// make white become transparent
				main.a = min(color.a, 1-color.r);
				
				return main;
			}
			
			ENDCG
		}
	}
}
1 Like

Does it still work?