Last active
July 18, 2025 06:06
-
-
Save mrkybe/55500910578619e038d1ed9a8eac2249 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Shader "Custom/SpriteColor" | |
| { | |
| Properties | |
| { | |
| [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} | |
| [HideInInspector] _Color ("Tint", Color) = (1,1,1,1) | |
| [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0 | |
| [HideInInspector] _RendererColor ("RendererColor", Color) = (1,1,1,1) | |
| [HideInInspector] _Flip ("Flip", Vector) = (1,1,1,1) | |
| [PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {} | |
| [PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0 | |
| _PixelsPerUnit ("Pixels Per Unit", Float) = 16 | |
| _Scale ("Scale", Float) = 1 | |
| _Radius ("Radius", Float) = 8 | |
| _OutlineColor("Outline Color", Color) = (0,0,0,1) | |
| _EdgeInnerColor("Edge Inner Color", Color) = (0.8,0.2,0,1) | |
| _BellyColor("Belly Color", Color) = (1,1,1,1) | |
| _FillColor("Fill Color", Color) = (0.5,0.5,0,1) | |
| _Angle("Angle", Float) = 0 | |
| } | |
| SubShader | |
| { | |
| Tags | |
| { | |
| "Queue"="Transparent" | |
| "IgnoreProjector"="True" | |
| "RenderType"="Transparent" | |
| "PreviewType"="Plane" | |
| "CanUseSpriteAtlas"="True" | |
| } | |
| Cull Off | |
| Lighting Off | |
| ZWrite Off | |
| Blend One OneMinusSrcAlpha | |
| Pass | |
| { | |
| CGPROGRAM | |
| #pragma vertex SpriteVert | |
| #pragma fragment SpriteFrag | |
| #pragma target 2.0 | |
| #pragma multi_compile_instancing | |
| #pragma multi_compile_local _ PIXELSNAP_ON | |
| #pragma multi_compile _ ETC1_EXTERNAL_ALPHA | |
| fixed4 _Color; | |
| float _PixelsPerUnit; | |
| float _Scale; | |
| float _Radius; | |
| float4 _OutlineColor; | |
| float4 _EdgeInnerColor; | |
| float4 _BellyColor; | |
| float4 _FillColor; | |
| float _Angle; | |
| struct appdata_t | |
| { | |
| float4 vertex : POSITION; | |
| float4 color : COLOR; | |
| float2 texcoord : TEXCOORD0; | |
| }; | |
| struct v2f | |
| { | |
| float4 vertex : SV_POSITION; | |
| fixed4 color : COLOR; | |
| float2 texcoord : TEXCOORD0; | |
| }; | |
| v2f SpriteVert(appdata_t IN) | |
| { | |
| v2f OUT; | |
| OUT.vertex = IN.vertex; | |
| OUT.vertex = UnityObjectToClipPos(OUT.vertex); | |
| OUT.texcoord = IN.texcoord; | |
| OUT.color = IN.color * _Color; | |
| return OUT; | |
| } | |
| sampler2D _MainTex; | |
| sampler2D _AlphaTex; | |
| fixed4 SampleSpriteTexture (float2 uv) | |
| { | |
| fixed4 color = tex2D (_MainTex, uv); | |
| return color; | |
| } | |
| float4 bhams(float2 uv, float radius, float2 uCenter) | |
| { | |
| int2 pixelCoord = int2(uv.xy * _PixelsPerUnit); | |
| float2 pcd = float2(0,0); | |
| uCenter *= _PixelsPerUnit; | |
| // Convert radius to integer | |
| int r = int(floor(radius + 0.5)); | |
| bool isOnCircle = false; | |
| bool isOutside = false; | |
| float depth = 0; | |
| float total = 0; | |
| int2 c = int2(floor(uCenter + 0.5)); | |
| int2 quadrants[4] = { | |
| int2(+1, +1), | |
| int2(-1, +1), | |
| int2(+1, -1), | |
| int2(-1, -1) | |
| }; | |
| int i = 0; | |
| // Initialize Bresenham's variables | |
| int x = 0; | |
| int y = r; | |
| int d = 3 - 2 * r; | |
| // reset Bresenham variables | |
| x = 0; | |
| y = r; | |
| d = 3 - 2 * r; | |
| while (x <= y) | |
| { | |
| int mx = x * quadrants[0].x; | |
| int my = y * quadrants[0].y; | |
| if(pixelCoord.x == c.x + mx && pixelCoord.y == c.y + my) | |
| { | |
| isOnCircle = true; | |
| } | |
| if(pixelCoord.x <= c.x + mx && pixelCoord.y <= c.y + my && | |
| pixelCoord.x >= c.x && pixelCoord.y >= c.y) | |
| { | |
| pcd.x += 0.5; | |
| } | |
| if (d < 0) | |
| { | |
| d += 4 * x + 6; | |
| } | |
| else | |
| { | |
| d += 4 * (x - y) + 10; | |
| y--; | |
| } | |
| x++; | |
| } | |
| x = 0; | |
| y = r; | |
| d = 3 - 2 * r; | |
| while (x <= y) | |
| { | |
| int mx = x * quadrants[1].x; | |
| int my = y * quadrants[1].y; | |
| if(pixelCoord.x == c.x + mx && pixelCoord.y == c.y + my) | |
| { | |
| isOnCircle = true; | |
| } | |
| if(pixelCoord.x >= c.x + mx && pixelCoord.y <= c.y + my && | |
| pixelCoord.x <= c.x && pixelCoord.y >= c.y) | |
| { | |
| pcd.x += 0.5; | |
| } | |
| if (d < 0) | |
| { | |
| d += 4 * x + 6; | |
| } | |
| else | |
| { | |
| d += 4 * (x - y) + 10; | |
| y--; | |
| } | |
| x++; | |
| } | |
| x = 0; | |
| y = r; | |
| d = 3 - 2 * r; | |
| while (x <= y) | |
| { | |
| int mx = x * quadrants[2].x; | |
| int my = y * quadrants[2].y; | |
| if(pixelCoord.x == c.x + mx && pixelCoord.y == c.y + my) | |
| { | |
| isOnCircle = true; | |
| } | |
| if(pixelCoord.x <= c.x + mx && pixelCoord.y >= c.y + my && | |
| pixelCoord.x >= c.x && pixelCoord.y <= c.y) | |
| { | |
| pcd.x += 0.5; | |
| } | |
| if (d < 0) | |
| { | |
| d += 4 * x + 6; | |
| } | |
| else | |
| { | |
| d += 4 * (x - y) + 10; | |
| y--; | |
| } | |
| x++; | |
| } | |
| x = 0; | |
| y = r; | |
| d = 3 - 2 * r; | |
| while (x <= y) | |
| { | |
| int mx = x * quadrants[3].x; | |
| int my = y * quadrants[3].y; | |
| if(pixelCoord.x == c.x + mx && pixelCoord.y == c.y + my) | |
| { | |
| isOnCircle = true; | |
| } | |
| if(pixelCoord.x >= c.x + mx && pixelCoord.y >= c.y + my && | |
| pixelCoord.x <= c.x && pixelCoord.y <= c.y) | |
| { | |
| pcd.x += 0.5; | |
| } | |
| if (d < 0) | |
| { | |
| d += 4 * x + 6; | |
| } | |
| else | |
| { | |
| d += 4 * (x - y) + 10; | |
| y--; | |
| } | |
| x++; | |
| } | |
| ///////////////////////////// | |
| x = 0; | |
| y = r; | |
| d = 3 - 2 * r; | |
| while (x <= y) | |
| { | |
| int mx = y * quadrants[0].x; | |
| int my = x * quadrants[0].y; | |
| if(pixelCoord.x == c.x + mx && pixelCoord.y == c.y + my) | |
| { | |
| isOnCircle = true; | |
| } | |
| if(pixelCoord.x <= c.x + mx && pixelCoord.y <= c.y + my && | |
| pixelCoord.x >= c.x && pixelCoord.y >= c.y) | |
| { | |
| pcd.x += 0.5; | |
| } | |
| if (d < 0) | |
| { | |
| d += 4 * x + 6; | |
| } | |
| else | |
| { | |
| d += 4 * (x - y) + 10; | |
| y--; | |
| } | |
| x++; | |
| } | |
| x = 0; | |
| y = r; | |
| d = 3 - 2 * r; | |
| while (x <= y) | |
| { | |
| int mx = y * quadrants[1].x; | |
| int my = x * quadrants[1].y; | |
| if(pixelCoord.x == c.x + mx && pixelCoord.y == c.y + my) | |
| { | |
| isOnCircle = true; | |
| } | |
| if(pixelCoord.x >= c.x + mx && pixelCoord.y <= c.y + my && | |
| pixelCoord.x <= c.x && pixelCoord.y >= c.y) | |
| { | |
| pcd.x += 0.5; | |
| } | |
| if (d < 0) | |
| { | |
| d += 4 * x + 6; | |
| } | |
| else | |
| { | |
| d += 4 * (x - y) + 10; | |
| y--; | |
| } | |
| x++; | |
| } | |
| x = 0; | |
| y = r; | |
| d = 3 - 2 * r; | |
| while (x <= y) | |
| { | |
| int mx = y * quadrants[2].x; | |
| int my = x * quadrants[2].y; | |
| if(pixelCoord.x == c.x + mx && pixelCoord.y == c.y + my) | |
| { | |
| isOnCircle = true; | |
| } | |
| if(pixelCoord.x <= c.x + mx && pixelCoord.y >= c.y + my && | |
| pixelCoord.x >= c.x && pixelCoord.y <= c.y) | |
| { | |
| pcd.x = 1; | |
| } | |
| if (d < 0) | |
| { | |
| d += 4 * x + 6; | |
| } | |
| else | |
| { | |
| d += 4 * (x - y) + 10; | |
| y--; | |
| } | |
| x++; | |
| } | |
| x = 0; | |
| y = r; | |
| d = 3 - 2 * r; | |
| while (x <= y) | |
| { | |
| int mx = y * quadrants[3].x; | |
| int my = x * quadrants[3].y; | |
| if(pixelCoord.x == c.x + mx && pixelCoord.y == c.y + my) | |
| { | |
| isOnCircle = true; | |
| } | |
| if(pixelCoord.x >= c.x + mx && pixelCoord.y >= c.y + my && | |
| pixelCoord.x <= c.x && pixelCoord.y <= c.y) | |
| { | |
| pcd.x = 1; | |
| } | |
| if (d < 0) | |
| { | |
| d += 4 * x + 6; | |
| } | |
| else | |
| { | |
| d += 4 * (x - y) + 10; | |
| y--; | |
| } | |
| x++; | |
| } | |
| // Run the Bresenham circle algorithm steps | |
| /* | |
| while (x <= y) | |
| { | |
| if (all(pixelCoord == int2(c.x + x, c.y + y)) || | |
| all(pixelCoord == int2(c.x - x, c.y + y)) || | |
| all(pixelCoord == int2(c.x + x, c.y - y)) || | |
| all(pixelCoord == int2(c.x - x, c.y - y)) || | |
| all(pixelCoord == int2(c.x + y, c.y + x)) || | |
| all(pixelCoord == int2(c.x - y, c.y + x)) || | |
| all(pixelCoord == int2(c.x + y, c.y - x)) || | |
| all(pixelCoord == int2(c.x - y, c.y - x))) | |
| { | |
| isOnCircle = true; | |
| break; | |
| } | |
| // Advance the Bresenham decision variable | |
| if (d < 0) | |
| { | |
| d += 4 * x + 6; | |
| } | |
| else | |
| { | |
| d += 4 * (x - y) + 10; | |
| y--; | |
| } | |
| x++; | |
| } | |
| */ | |
| // Output color if the pixel is on the circle, else discard | |
| return float4(isOnCircle, pcd.x, 0, isOnCircle || pcd.x > 0); | |
| } | |
| fixed4 SpriteFrag(v2f IN) : SV_Target | |
| { | |
| float2 center = float2(0.5, 0.5); | |
| float4 ol1 = bhams(IN.texcoord, _Radius, center); | |
| float4 ol2 = bhams(IN.texcoord, _Radius, center); | |
| float olmix = (ol1.r || ol2.r) || (ol1.g && !ol2.g); | |
| float4 outline = _OutlineColor * olmix; | |
| float4 il1 = bhams(IN.texcoord, _Radius - (_Radius < 16.5 ? 0 : 0), center); | |
| float4 il2 = bhams(IN.texcoord, _Radius - (_Radius < 16.5 ? 1 : 1), center); | |
| float ilmix = (il1.r || il2.r) || (il1.g && !il2.g); | |
| float4 in_line = _EdgeInnerColor * ilmix; | |
| float2 bellyPos = float2(sin(_Angle), cos(_Angle)) * (floor(_Radius)/100); | |
| float4 belly = bhams(IN.texcoord, _Radius, center + bellyPos); | |
| float bellymix = ((belly.r || belly.g) && ol1.g) && !olmix; | |
| float bellyLinemix = ((belly.r || belly.g) && ilmix) && !olmix; | |
| belly = _BellyColor * (bellymix - bellyLinemix) + _FillColor * bellyLinemix; | |
| float4 fcol = outline + in_line * abs(outline.a - in_line.a); | |
| float4 ffcol = belly + fcol * abs(fcol.a - belly.a); | |
| float fmix = !ffcol.a && ol2.g; | |
| float4 fill = _FillColor * fmix; | |
| return saturate(ffcol + fill); | |
| } | |
| ENDCG | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment