Hi all,
I’m trying to get pixel perfect circles into a game, something like “Hundreds”. So far I’ve had difficulty getting any sort of decent performance on iPhone 4 (familiar story right?).
What seemed to be my best bet, has turned out to be the slowest of the lot- and that’s writing a custom vert/frag shader that shades the circle.
The shader beneath for some reason is super-slow on the iphone 4 (I’ve tried tweaking precision to be fixed/half for as many of the variables as possible, but with no real gain). 80 circles using this shader is taking 20-30ms to draw- which is obviously unacceptable.
Note, you can test this shader on a quad (although I have a circle mesh making script that draws a coarse circle, but to show actual texture, you’d need procedural geometry, where the x/y vertices are also stored in uv2. The reason for this is that view-space shading like this doesn’t work when batching is on.)
I think at least part of the problem must be that with 80 circles on screen, all alpha blended, all calculating circles length per pixel is just too much for the humble GPU.
Shader "Sprite/CircleClipLocalSpace"
{
Properties
{
_MainTex ("Base (RGB), Alpha (A)", 2D) = "white" {}
_Edge ("Edge", Float) = 2
_Strength ("Strength", Range(0,1)) = 0.5
}
SubShader
{
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
Pass
{
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
Lighting Off
ZWrite Off
Fog { Color (0,0,0,0) }
CGPROGRAM
#pragma exclude_renderers d3d11 xbox360
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#pragma glsl
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
float _Edge;
float _Strength;
struct v2f
{
half4 vertex : POSITION;
half2 texcoord : TEXCOORD0;
half2 savedVertices : TEXCOORD1;
fixed4 color : COLOR;
half2 texUV : TEXCOORD2;
};
struct appdata_t
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
float2 savedVertices : TEXCOORD1;
float4 color : COLOR;
};
inline float4 Overlay (float4 a, float4 b) {
return lerp( 1 - 2 * (1 - a) * (1 - b), 2 * a * b, step( a, 0.5 ));
}
v2f vert (appdata_t v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.texcoord = v.texcoord - half2(0.5,0.5);
o.color = v.color;
o.texUV = v.savedVertices * _MainTex_ST.xy + _MainTex_ST.w;
return o;
}
half4 frag (v2f IN) : COLOR
{
fixed4 col = Overlay(IN.color, tex2D(_MainTex, IN.texUV) * _Strength);
fixed4 transparent = fixed4(col.xyz,0);
float l = length(IN.texcoord);
float thresholdWidth = length(float2(ddx(l),ddy(l))) * _Edge;
float antialiasedCircle = saturate(((1.0 - ( thresholdWidth * 0.25) - (l * 2)) / thresholdWidth) + 0.5) ;
return lerp(transparent, col, antialiasedCircle);
}
ENDCG
}
}
}
Other approaches I’ve tried are:
-Large 2048x2048 circle sprite. This was okayish, but showed some jagged edges or edge blurred at high zooms
-High poly circle mesh with MSAA on a 2x (too slow on iPhone 4 / iPad 1)
-Optimising this shader by: Removing the overlay (helped a bit, just multiplying the IN.color), and removing the color component entirely (i.e. baking the color into the textures)
Does anyone have anything else to suggest? I’m struggling with ideas on how to do this. My last hope is to attempt to shade the interior of the circle using an opaque shader, and then attempt to shade the edges with this blend shader, using a separate mesh, but I worry that that’s going to create its own problems. Am I missing something obvious?
Thanks!
Mike