Shader "Hidden/ChocDino/UIFX/DistanceMap" { Properties { [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} [PerRendererData] _InsideTex ("Inside Texture", 2D) = "white" {} _StepSize ("Step Size", Vector) = (1.0, 1.0, 0.0, 0.0) _DownSample ("Down Sample", Int) = 1 [KeywordEnum(Square,Diamond,Circle)] Dist("Dist",int) = 0 } CGINCLUDE struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; float2 uv : TEXCOORD0; }; uniform sampler2D _MainTex; uniform sampler2D _InsideTex; uniform float4 _MainTex_TexelSize; uniform float2 _StepSize; uniform int _DownSample; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o; } // Manhattan distance float distanceDiamond(float2 a, float2 b) { return abs(a.x - b.x) + abs(a.y - b.y); } // Chebyshev distance float distanceSquare(float2 a, float2 b) { return max(abs(a.x - b.x), abs(a.y - b.y)); } #define NULL_PIXEL -1.0 float2 fragAlphaToUV(v2f i) : SV_Target { float2 offset = _MainTex_TexelSize.xy; float2 result = i.uv; float alpha = tex2D(_MainTex, i.uv).a; if (alpha > 0.99) { return result; } // Changed this from 0.01 to 0.016 to filter out some very tiny values introduced in some textues when using HIGH compression setting if (alpha < 0.016) { return NULL_PIXEL; } // For intermediate alpha values use sobel filter to estimate the fractional offset of the subpixel // Credit for this idea to Ben Golus "The Quest for Very Wide Outlines". float c00 = tex2D(_MainTex, i.uv - offset.xy).a; float c10 = tex2D(_MainTex, i.uv + float2(0.0, -offset.y)).a; float c20 = tex2D(_MainTex, i.uv + float2(offset.x, -offset.y)).a; float c01 = tex2D(_MainTex, i.uv + float2(-offset.x, 0.0)).a; float c21 = tex2D(_MainTex, i.uv + float2(offset.x, 0.0)).a; float c02 = tex2D(_MainTex, i.uv + float2(-offset.x, offset.y)).a; float c12 = tex2D(_MainTex, i.uv + float2(0.0, offset.y)).a; float c22 = tex2D(_MainTex, i.uv + float2(offset.x, offset.y)).a; // Sobel gradient to estimate edge direction float sobelX = c00 + c01 * 2.0 + c02 - c20 - c21 * 2.0 - c22; float sobelY = c00 + c10 * 2.0 + c20 - c02 - c12 * 2.0 - c22; float2 dir = -float2(sobelX, sobelY); // If dir length is small, this is either a sub pixel dot or line // no way to estimate sub pixel edge, so output position if (abs(dir.x) <= 0.005 && abs(dir.y) <= 0.005) { return result; } // normalize direction dir = normalize(dir); // sub pixel offset offset *= dir * (1.0 - alpha); result = (i.uv + offset); return result; } float2 fragInvAlphaToUV(v2f i) : SV_Target { float2 offset = _MainTex_TexelSize.xy; float2 result = i.uv; float alpha = 1.0-tex2D(_MainTex, i.uv).a; if (alpha > 0.99) { return result; } if (alpha < 0.01) { return NULL_PIXEL; } // For intermediate alpha values use sobel filter to estimate the fractional offset of the subpixel // Credit for this idea to Ben Golus "The Quest for Very Wide Outlines". float c00 = tex2D(_MainTex, i.uv - offset.xy).a; float c10 = tex2D(_MainTex, i.uv + float2(0.0, -offset.y)).a; float c20 = tex2D(_MainTex, i.uv + float2(offset.x, -offset.y)).a; float c01 = tex2D(_MainTex, i.uv + float2(-offset.x, 0.0)).a; float c21 = tex2D(_MainTex, i.uv + float2(offset.x, 0.0)).a; float c02 = tex2D(_MainTex, i.uv + float2(-offset.x, offset.y)).a; float c12 = tex2D(_MainTex, i.uv + float2(0.0, offset.y)).a; float c22 = tex2D(_MainTex, i.uv + float2(offset.x, offset.y)).a; // Sobel gradient to estimate edge direction float sobelX = c00 + c01 * 2.0 + c02 - c20 - c21 * 2.0 - c22; float sobelY = c00 + c10 * 2.0 + c20 - c02 - c12 * 2.0 - c22; float2 dir = float2(sobelX, sobelY); // If dir length is small, this is either a sub pixel dot or line // no way to estimate sub pixel edge, so output position if (abs(dir.x) <= 0.005 && abs(dir.y) <= 0.005) { return result; } // normalize direction dir = normalize(dir); // sub pixel offset offset *= dir * (1.0 - alpha); result = (i.uv + offset); return result; } float2 fragJumpFlood(v2f i) : SV_Target { float2 pixelScale = _MainTex_TexelSize.zw; float2 offset = _MainTex_TexelSize.xy * _StepSize; float2 closest = NULL_PIXEL; // NOTE: this value should be big enough, as using 1.#INF was causing problems with some console compilers #if SHADER_API_GLES || SHADER_API_MOBILE || SHADER_API_SWITCH float minDist = 32768.0; #else float minDist = 16777216.0; #endif float2 p0 = tex2D(_MainTex, i.uv).xy; if (p0.x > NULL_PIXEL) { float2 v3 = (p0 - i.uv) * pixelScale; float dd = dot(v3, v3); if (dd < 2)//(abs(p0.x - i.uv.x) < _MainTex_TexelSize.x*1.5) && (abs(p0.y - i.uv.y) < _MainTex_TexelSize.y*1.5)) { return p0; } closest = p0; #if DIST_CIRCLE minDist = dd;//dot(v2, v2); //float sd = distance(p * pixelScale, i.uv * pixelScale); #elif DIST_DIAMOND minDist = distanceDiamond(p0 * pixelScale, i.uv * pixelScale); #elif DIST_SQUARE minDist = distanceSquare(p0 * pixelScale, i.uv * pixelScale); #endif } UNITY_UNROLL for (int y = -1; y <= 1; y++) { UNITY_UNROLL for (int x = -1; x <= 1; x++) { if (x == 0 && y == 0) continue; float2 p = tex2D(_MainTex, i.uv + offset * float2(x, y)).xy; if (p.x > NULL_PIXEL) { float sd = 0.0; #if DIST_CIRCLE float2 v = (p - i.uv) * pixelScale; sd = dot(v, v); //float sd = distance(p * pixelScale, i.uv * pixelScale); #elif DIST_DIAMOND sd = distanceDiamond(p * pixelScale, i.uv * pixelScale); #elif DIST_SQUARE sd = distanceSquare(p * pixelScale, i.uv * pixelScale); #endif if (sd < minDist) { minDist = sd; closest = p; } } } } return closest; } float2 fragJumpFloodSingleAxis(v2f i) : SV_Target { float2 pixelScale = _MainTex_TexelSize.zw; float2 offset = _MainTex_TexelSize.xy * _StepSize; float2 closest = NULL_PIXEL; // NOTE: this value should be big enough, as using 1.#INF was causing problems with some console compilers #if SHADER_API_GLES || SHADER_API_MOBILE || SHADER_API_SWITCH float minDist = 32768.0; #else float minDist = 16777216.0; #endif float2 p0 = tex2D(_MainTex, i.uv).xy; if (p0.x > NULL_PIXEL) { float2 v3 = (p0 - i.uv) * pixelScale; float dd = dot(v3, v3); if (dd < 2)//(abs(p0.x - i.uv.x) < _MainTex_TexelSize.x*1.5) && (abs(p0.y - i.uv.y) < _MainTex_TexelSize.y*1.5)) { return p0; } closest = p0; #if DIST_CIRCLE //float2 v2 = (p0 - i.uv) * pixelScale; minDist = dd;//dot(v2, v2); //float sd = distance(p * pixelScale, i.uv * pixelScale); #elif DIST_DIAMOND minDist = distanceDiamond(p0 * pixelScale, i.uv * pixelScale); #elif DIST_SQUARE minDist = distanceSquare(p0 * pixelScale, i.uv * pixelScale); #endif } UNITY_UNROLL for (int x = -1; x <= 1; x++) { if (x == 0) continue; float2 p = tex2D(_MainTex, i.uv + offset * float2(x, x)).xy; if (p.x > NULL_PIXEL) { float sd = 0.0; #if DIST_CIRCLE float2 v = (p - i.uv) * pixelScale; sd = dot(v, v); //float sd = distance(p * pixelScale, i.uv * pixelScale); #elif DIST_DIAMOND sd = distanceDiamond(p * pixelScale, i.uv * pixelScale); #elif DIST_SQUARE sd = distanceSquare(p * pixelScale, i.uv * pixelScale); #endif if (sd < minDist) { minDist = sd; closest = p; } } } return closest; } // NOTE: In GLES2.0 if you return float it gives an error "Type mismatch, cannot convert from 'float' to 'vec4'", so just return float4 float4 fragResolveDistance(v2f i) : SV_Target { float2 pixelScale = _MainTex_TexelSize.zw; float2 p0 = i.uv * pixelScale; float2 p1 = tex2D(_MainTex, i.uv).xy * pixelScale; float d = 0.0; //if (p1.x > NULL_PIXEL) { #if DIST_CIRCLE d = distance(p0, p1); #elif DIST_DIAMOND d = distanceDiamond(p0, p1); #elif DIST_SQUARE d = distanceSquare(p0, p1); #endif // Subtract to eliminate noise where distances are almost equal // TODO: ideally shouldn't even write these pixels, just zero them //d = max(0, d - 2.0); } return d * _DownSample; } // NOTE: In GLES2.0 if you return float it gives an error "Type mismatch, cannot convert from 'float' to 'vec4'", so just return float4 float4 fragResolveDistanceIOnOutMax(v2f i) : SV_Target { float2 pixelScale = _MainTex_TexelSize.zw; float2 p0 = i.uv * pixelScale; float2 p1 = tex2D(_MainTex, i.uv).xy * pixelScale; float dOut = 0.0; #if DIST_CIRCLE dOut = distance(p0, p1); #elif DIST_DIAMOND dOut = distanceDiamond(p0, p1); #elif DIST_SQUARE dOut = distanceSquare(p0, p1); #endif p1 = tex2D(_InsideTex, i.uv).xy * pixelScale; float dIn = 0.0; #if DIST_CIRCLE dIn = distance(p0, p1); #elif DIST_DIAMOND dIn = distanceDiamond(p0, p1); #elif DIST_SQUARE dIn = distanceSquare(p0, p1); #endif float d = max(dOut, dIn); return (d * _DownSample); } // NOTE: In GLES2.0 if you return float it gives an error "Type mismatch, cannot convert from 'float' to 'vec4'", so just return float4 float4 fragResolveDistanceSDF(v2f i) : SV_Target { float2 pixelScale = _MainTex_TexelSize.zw; float2 p0 = i.uv * pixelScale; float2 p1 = tex2D(_MainTex, i.uv).xy * pixelScale; float dOut = 0.0; #if DIST_CIRCLE dOut = distance(p0, p1); #elif DIST_DIAMOND dOut = distanceDiamond(p0, p1); #elif DIST_SQUARE dOut = distanceSquare(p0, p1); #endif p1 = tex2D(_InsideTex, i.uv).xy * pixelScale; float dIn = 0.0; #if DIST_CIRCLE dIn = distance(p0, p1); #elif DIST_DIAMOND dIn = distanceDiamond(p0, p1); #elif DIST_SQUARE dIn = distanceSquare(p0, p1); #endif float d = dOut - dIn; return (d * _DownSample); } ENDCG SubShader { Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" "CanUseSpriteAtlas"="True" } Cull Off ZWrite Off ZTest Always Pass { Name "AlphaToUV" CGPROGRAM #pragma vertex vert #pragma fragment fragAlphaToUV ENDCG } Pass { Name "InvAlphaToUV" CGPROGRAM #pragma vertex vert #pragma fragment fragInvAlphaToUV ENDCG } Pass { Name "JumpFlood" CGPROGRAM #pragma vertex vert #pragma fragment fragJumpFlood #pragma multi_compile_local_fragment DIST_SQUARE DIST_DIAMOND DIST_CIRCLE ENDCG } Pass { Name "JumpFloodSingleAxis" CGPROGRAM #pragma vertex vert #pragma fragment fragJumpFloodSingleAxis #pragma multi_compile_local_fragment DIST_SQUARE DIST_DIAMOND DIST_CIRCLE ENDCG } Pass { Name "ResolveDistance" CGPROGRAM #pragma vertex vert #pragma fragment fragResolveDistance #pragma multi_compile_local_fragment DIST_SQUARE DIST_DIAMOND DIST_CIRCLE ENDCG } Pass { Name "ResolveDistanceInOutMax" CGPROGRAM #pragma vertex vert #pragma fragment fragResolveDistanceIOnOutMax #pragma multi_compile_local_fragment DIST_SQUARE DIST_DIAMOND DIST_CIRCLE ENDCG } Pass { Name "ResolveDistanceSDF" CGPROGRAM #pragma vertex vert #pragma fragment fragResolveDistanceSDF #pragma multi_compile_local_fragment DIST_SQUARE DIST_DIAMOND DIST_CIRCLE ENDCG } } }