Wow, an interesting question and even a reward!

Generally, how much I did not read, I could not imagine why the effect of blur to apply on transparency. After all, if a certain part of the sprite is transparent, then there will be no blur and so visible and there, and where there is no transparency, then there is no blur. Either I didn’t understand what it was and it meant that the application of the blur in general to the whole sprite, regardless of what percentage of the radial fill is now available. If so, then
- You can do what is written in another answer: take Unity from the standard assemblies (the benefit is not a little provided) the blur effect applied on the camera. Add another camera, add a blur script and a shader there and turn on that camera at the right moment and change the offset in the Blur effect.
- Implement, as they wanted in the shader)) About this at the very end.
(!!!)
May the site admins forgive me, but I don’t have one answer (because of the amount of code, and not because of "water"). Therefore, the answer will be in two parts.
The answer, part 1.
The point is that the trimming, which is flooded with transparency will be reduced to what will be taken mask, on the basis of which everything will happen. The mask is generated programmatically via the atan2 trigonometric function. On axes, it looks like this:

In a two-dimensional coordinate system it looks like this:

Since the mask is a kind of component consisting of shades of black and white, in which black is the complete absence of texture and white is completely visible texture, for transparency the atan2 mask will be a transition from black to white (see the picture above ), and for trimming an additional function will be applied, so that there is only black / white, without smooth transitions.

I will try to just post a shader, which will be comments on what has been done. I’m not sure that everything will be clear, but I’ll try to do something really bad.
Shader "Custom/RadialFill" { Properties { [PerRendererData]_MainTex ("MainTex", 2D) = "white" {} _Color ("Color", Color) = (1,1,1,1) _OpacityRotator ("Opacity Rotator", Range(-360, 360)) = -360 // два полных оборота _TextureRotator ("Texture Rotator", Range(0, 360)) = 360 [MaterialToggle] _FillClockwise ("Fill Clockwise", Float ) = 1 [HideInInspector]_Cutoff ("Alpha cutoff", Range(0,1)) = 0.5 [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0 [KeywordEnum(Right, Bottom, Left, Top)] _Fill_Origin("Fill Origin", Int) = 0 } SubShader { // https://docs.unity3d.com/ru/current/Manual/SL-SubShaderTags.html Tags { "IgnoreProjector"="True" "Queue"="Transparent" "RenderType"="Transparent" "CanUseSpriteAtlas"="True" "PreviewType"="Plane" } Pass { Name "FORWARD" Tags { "LightMode"="ForwardBase" // https://docs.unity3d.com/Manual/SL-PassTags.html } Blend One OneMinusSrcAlpha // https://docs.unity3d.com/ru/current/Manual/SL-Blend.html ZWrite Off // https://docs.unity3d.com/ru/current/Manual/SL-CullAndDepth.html CGPROGRAM #pragma vertex vert // vert - имя функции обработки вершин #pragma fragment frag // frag - имя функции обработки пикселей #pragma multi_compile _ PIXELSNAP_ON // как работает shader_feature: https://docs.unity3d.com/ru/530/Manual/SL-MultipleProgramVariants.html // он относится к свойству _Fill_Origin .... по сути - автоматически конвертируем его имя и значения в константы #pragma shader_feature _FILL_ORIGIN_RIGHT _FILL_ORIGIN_BOTTOM _FILL_ORIGIN_LEFT _FILL_ORIGIN_TOP #include "UnityCG.cginc" #pragma exclude_renderers gles3 metal d3d11_9x xbox360 xboxone ps3 ps4 psp2 #pragma target 3.0 uniform sampler2D _MainTex; uniform float4 _MainTex_ST; uniform float4 _Color; uniform float _OpacityRotator; uniform float _TextureRotator; uniform fixed _FillClockwise; static const float TAU = float(6.283185); // это 2 * PI, кто не знает struct VertexInput { float4 vertex : POSITION; float3 normal : NORMAL; float4 tangent : TANGENT; float2 texcoord0 : TEXCOORD0; }; struct VertexOutput { float4 pos : SV_POSITION; float2 uv0 : TEXCOORD0; float4 posWorld : TEXCOORD1; float3 normalDir : TEXCOORD2; float3 tangentDir : TEXCOORD3; float3 bitangentDir : TEXCOORD4; }; VertexOutput vert (VertexInput v) { VertexOutput o = (VertexOutput)0; o.uv0 = v.texcoord0; o.normalDir = UnityObjectToWorldNormal(v.normal); o.tangentDir = normalize(mul(_Object2World, float4(v.tangent.xyz, 0.0)).xyz); o.bitangentDir = normalize(cross(o.normalDir, o.tangentDir) * v.tangent.w); o.posWorld = mul(_Object2World, v.vertex); o.pos = mul(UNITY_MATRIX_MVP, v.vertex ); #ifdef PIXELSNAP_ON o.pos = UnityPixelSnap(o.pos); #endif return o; } float4 frag(VertexOutput i) : COLOR { i.normalDir = normalize(i.normalDir); float4 _MainTex_var = tex2D(_MainTex,TRANSFORM_TEX(i.uv0, _MainTex)); /*** Общее начало для opacity и cutoff, помогающее переключать вращение по/против часовой стрелки BEGIN ***/ // float2(1, -1) - по часовой, float2(1, 1) - против часовой float2 clockCounterDirection = _FillClockwise ? float2(1, -1) : float2(1, 1); // по умолчанию "обрезание" начинается слева. // умножение на -1 для того, чтоб началось справа.....просто потому, что я так хочу =) float2 CommonStartAndSwitcher = (-1 * (i.uv0 - 0.5)) * clockCounterDirection; /*** Общее начало для opacity и cutoff с переключателем вращения по/против часовой стрелки END ***/ /*** Секция для cutoff ***/ float cutoffRightBottomLeftTop = 1.0; // изменение направления // В зависимости от того, что выбрано в качестве старта вращения право/лево/верх/низ // нужно будет провернуть и текстурку. // +0.25 - 90 градусов, +0.5 - 180, +0.75 - 270 #if _FILL_ORIGIN_BOTTOM cutoffRightBottomLeftTop = _FillClockwise ? 1.75 : 1.25; #elif _FILL_ORIGIN_LEFT cutoffRightBottomLeftTop = 1.5; #elif _FILL_ORIGIN_TOP cutoffRightBottomLeftTop = _FillClockwise ? 1.25 : 1.75; #endif cutoffRightBottomLeftTop += 0.001; // Матрица вращения для cutoff float cutoffRotator_ang = cutoffRightBottomLeftTop * -TAU; float cutoffRotator_cos = cos(cutoffRotator_ang); float cutoffRotator_sin = sin(cutoffRotator_ang); float2x2 cutoffRotationMatrix = float2x2(cutoffRotator_cos, -cutoffRotator_sin, cutoffRotator_sin, cutoffRotator_cos); float2 cutoffRotator = mul(CommonStartAndSwitcher, cutoffRotationMatrix); // перевод из системы от 0 до 360 градусов в отсчет от 0 до 1 float tRotatorNormalized = _TextureRotator / 360.0; // Генерирование маски для отсечения пикселей и отсечение пикселей по предоставленной маске // 1. Для генерации нужны исхоные две координаты.... // rg, утрированно, представляют из себя x и y float2 cutoffMaskSource = cutoffRotator.rg; // 2. Формируем начальную маску // в инете рисуночки глянуть как это выглядит =) // Угол задается в радианах и принимает значения от -PI до PI, исключая -PI float atan2Mask = atan2(cutoffMaskSource.g, cutoffMaskSource.r); // 3. Добавляем пол оборота (до целого) и конвертируем в значение от 0 до 1, // для дальнейшей удобной работы в единичном отрезке, т.к tRotatorNormalized меняется от 0 до 1 float atan2MaskNormalized = (atan2Mask / TAU) + 0.5; // 4. Привязка маски к повороту. хз как объяснить float atan2MaskRotatable = atan2MaskNormalized - tRotatorNormalized; // 5. Получаем карту заливки от белого к черному // Белый - полностью видимый участок, Черный - обрезающиеся (не отображающиеся) пиксели float whiteToBlackMask = ceil(atan2MaskRotatable); // 6. Собираем финальную маску от чёрного к белому (т.к. нужно постепенное заполнение) float finalMask = 1.0 - whiteToBlackMask; clip(finalMask - 0.5); /*** Секция для opacity ***/ // oVector меняется в зависимости от начала направления - лево/право/верх/низ float2 oVector = float2(1, -1); // изменение направления в зависимости от лево-право (1.0) или верх-низ (-1.0) float oRightBottomLeftTop = 1.0; // В зависимости от того, что выбрано в качестве старта вращения право/лево/верх/низ // нужно будет провернуть и маску. #if _FILL_ORIGIN_LEFT oVector = float2(-1, 1); #elif _FILL_ORIGIN_TOP oVector = _FillClockwise ? float2(-1, -1) : float2(1, 1); oRightBottomLeftTop = -1.0; #elif _FILL_ORIGIN_BOTTOM oVector = _FillClockwise ? float2(1, 1) : float2(-1, -1); oRightBottomLeftTop = -1.0; #endif float oRotatorNormalized = _OpacityRotator / 360.0; // Матрица вращения для opacity float oRotator_ang = oRightBottomLeftTop * (oRotatorNormalized * -TAU); float oRotator_cos = cos(oRotator_ang); float oRotator_sin = sin(oRotator_ang); float2x2 oRotationMatrix = float2x2(oRotator_cos, -oRotator_sin, oRotator_sin, oRotator_cos); float2 oRotator = mul(oVector * CommonStartAndSwitcher, oRotationMatrix); // Как и у cutoff формируем маску float2 oMask = oRotator.rg; float2 oMaskHorizOrVert = atan2(oMask.g, oMask.r); // при формировании маски по вертикали, нужно поменять x, y местами в функции #if (_FILL_ORIGIN_TOP || _FILL_ORIGIN_BOTTOM) oMaskHorizOrVert = atan2(oMask.r, oMask.g); #endif float oAtan2MaskNormalized = (oMaskHorizOrVert / TAU) + 0.5; // oRotatorNormalized - oAtan2MaskNormalized для того, чтобы первый круг просто провернуться, а на втором // начать обрезку как у cutoff, только начиная схвоста, но при этом продолжая вращаться. // Если было бы oAtan2MaskNormalized - oRotatorNormalized (как в примере с cutoff выше), то, т.к. значение oRotatorNormalized // меняется с -1 до 1 (два полных круга), получается что маска наложена на изображение 2 раза: 1 раз - прозрачность, 2 раз - она же // поэтому увеличивается наложенность, белый цвет. В итоге при изменении с -1 до 1 ушла бы в начале белизна, а потом провернулась бы маска, // и не обрезалась бы float oAtan2MaskRotatable = oRotatorNormalized - oAtan2MaskNormalized; float oWhiteToBlackMask = ceil(oAtan2MaskRotatable); // Финальная прозрачность float oFinalMultiply = _MainTex_var.a * max(oAtan2MaskNormalized, ceil(oWhiteToBlackMask)); /*** Излучение (Emissive) ***/ // oFinalMultiply чтоб обрезать прозрачную область, где она обрезана в самой текстуре float3 finalColor = _MainTex_var.rgb * _Color.rgb * oFinalMultiply; // Конечный результат (цвет, обработанный маской и повернутый под углом альфа канал) return fixed4(finalColor, oFinalMultiply); } ENDCG } } FallBack "Diffuse" }
Чтобы в нужный момент запустить заливку, конечно же нужно дать команду. А откуда её можно дать? Правильно — из скрипта. Он будет расположен ниже:
using UnityEngine; using System.Collections; public class RadialFill : MonoBehaviour { public float cutoffStartAngle = 5.0f; // градусы public float opacityStartAngle = -350.0f; // градусы, -2 * PI + 10 (небольшой начальный угол) public float deltaAngle = 5f; private const float MAX_ANGLE = 360.0f; private Material material; private float _TextureRotator; // ссылка на переменную _TextureRotator в шейдере private float _OpacityRotator; // ссылка на переменную _TextureRotator в шейдере void Start () { material = GetComponent<SpriteRenderer>().material; } void Update () { if (Input.GetMouseButtonDown(0)) //if (Input.GetKeyDown("f")) StartCoroutine(FillSprite()); } IEnumerator FillSprite() { var cOffStart = cutoffStartAngle; var oStart = opacityStartAngle; material.SetFloat("_TextureRotator", cOffStart); material.SetFloat("_OpacityRotator", oStart); _TextureRotator = cOffStart; _OpacityRotator = oStart; while(_OpacityRotator <= MAX_ANGLE) { if (_TextureRotator >= MAX_ANGLE) _TextureRotator = MAX_ANGLE; if (_OpacityRotator >= MAX_ANGLE) _OpacityRotator = MAX_ANGLE; material.SetFloat("_TextureRotator", _TextureRotator); material.SetFloat("_OpacityRotator", _OpacityRotator); _OpacityRotator += deltaAngle; _TextureRotator += deltaAngle; yield return null; } yield break; } }
Where:
cutoffStartAngle — начальный угол обрезки, opacityStartAngle — начальный угол прозрачности. Эти параметры для того, чтобы немного отрегулировать по вкусу площадь сектора, занимаемого прозрачностью. Замечу, что прозрачность изменяется от -360 до 360, потому что первый круг она проворачивается сама по себе, а второй круг — плавно "заходит" за текстуру.
deltaAngle - дельта, на которую проворачиваются маски.
Что скрипт делает? При нажатии нажатии на клавишу мыши он берет шейдер у спрайта (точнее с его материала), устанавливает изначальные углы, в цикле изменяет угол поворота и передает это значение в шейдер, чтобы он там уже у себя применил значения в frag .
Выглядит в инспекторе так:

- Opacity Rotator - вращение маски прозрачности
- Texture Rotator - вращение маски обрезки
- Fill Clockwise - по часовой стрелке или против
- Fill Origin - с какой стороны начинать (справа/слева/сверху/снизу)
Итог будет выглядеть примерно таким:

Увы на данной гифке не получается передать то, как это выглядит в правильности
Улучшение
Так как у секции cutoff и opacity есть общие части, то их можно вынести в общие функции, как и во всех нормальных языках программирования.
Shader "Custom/RadialFillCommonFunctions" { Properties { [PerRendererData]_MainTex ("MainTex", 2D) = "white" {} _Color ("Color", Color) = (1,1,1,1) _OpacityRotator ("Opacity Rotator", Range(-360, 360)) = -360 // два полных оборота _TextureRotator ("Texture Rotator", Range(0, 360)) = 360 [MaterialToggle] _FillClockwise ("Fill Clockwise", Float ) = 1 [HideInInspector]_Cutoff ("Alpha cutoff", Range(0,1)) = 0.5 [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0 [KeywordEnum(Right, Bottom, Left, Top)] _Fill_Origin("Fill Origin", Int) = 0 } SubShader { Tags { "IgnoreProjector"="True" "Queue"="Transparent" "RenderType"="Transparent" "CanUseSpriteAtlas"="True" "PreviewType"="Plane" } Pass { Name "FORWARD" Tags { "LightMode"="ForwardBase" } Blend One OneMinusSrcAlpha Cull Off ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment frag #define UNITY_PASS_FORWARDBASE #pragma multi_compile _ PIXELSNAP_ON #pragma shader_feature _FILL_ORIGIN_RIGHT _FILL_ORIGIN_BOTTOM _FILL_ORIGIN_LEFT _FILL_ORIGIN_TOP #include "UnityCG.cginc" #pragma multi_compile_fwdbase #pragma exclude_renderers gles3 metal d3d11_9x xbox360 xboxone ps3 ps4 psp2 #pragma target 3.0 static const float TAU = float(6.283185); // это 2 * PI, кто не знает uniform sampler2D _MainTex; uniform float4 _MainTex_ST; uniform float4 _Color; uniform float _OpacityRotator; uniform float _TextureRotator; uniform fixed _FillClockwise; struct VertexInput { float4 vertex : POSITION; float3 normal : NORMAL; float4 tangent : TANGENT; float2 texcoord0 : TEXCOORD0; }; struct VertexOutput { float4 pos : SV_POSITION; float2 uv0 : TEXCOORD0; float4 posWorld : TEXCOORD1; float3 normalDir : TEXCOORD2; float3 tangentDir : TEXCOORD3; float3 bitangentDir : TEXCOORD4; }; // матрица вращения float2x2 getMatrix(float angle) { float r_cos = cos(angle); float r_sin = sin(angle); return float2x2(r_cos, -r_sin, r_sin, r_cos); } // формирование маски float2x2 getMask(float oAtan2MaskNormalized, float rotator, int isRotatorSubtract) { //float atan2var = reverseMaskCoords ? atan2(maskChannels.r, maskChannels.g) : atan2(maskChannels.g, maskChannels.r); //float oAtan2MaskNormalized = (atan2var / TAU) + 0.5; float oAtan2MaskRotatable = isRotatorSubtract ? oAtan2MaskNormalized - rotator : rotator - oAtan2MaskNormalized; return ceil(oAtan2MaskRotatable); } float getNormalizedAtanMask(float2 maskChannels, int reverseMaskCoords) { float atan2var = reverseMaskCoords ? atan2(maskChannels.r, maskChannels.g) : atan2(maskChannels.g, maskChannels.r); return (atan2var / TAU) + 0.5; } VertexOutput vert (VertexInput v) { VertexOutput o = (VertexOutput)0; o.uv0 = v.texcoord0; o.normalDir = UnityObjectToWorldNormal(v.normal); o.tangentDir = normalize(mul(_Object2World, float4(v.tangent.xyz, 0.0)).xyz); o.bitangentDir = normalize(cross(o.normalDir, o.tangentDir) * v.tangent.w); o.posWorld = mul(_Object2World, v.vertex); o.pos = mul(UNITY_MATRIX_MVP, v.vertex ); #ifdef PIXELSNAP_ON o.pos = UnityPixelSnap(o.pos); #endif return o; } float4 frag(VertexOutput i) : COLOR { i.normalDir = normalize(i.normalDir); float4 _MainTex_var = tex2D(_MainTex,TRANSFORM_TEX(i.uv0, _MainTex)); /*** Общее начало для opacity и cutoff, помогающее переключать вращение по/против часовой стрелки BEGIN ***/ // float2(1, -1) - по часовой, float2(1, 1) - против часовой float2 clockCounterDirection = _FillClockwise ? float2(1, -1) : float2(1, 1); // по умолчанию "обрезание" начинается слева. // умножение на -1 для того, чтоб началось справа.....просто потому, что я так хочу =) float2 CommonStartAndSwitcher = (-1 * (i.uv0 - 0.5)) * clockCounterDirection; /*** Общее начало для opacity и cutoff с переключателем вращения по/против часовой стрелки END ***/ /*** Секция для cutoff ***/ float tRotatorNormalized = _TextureRotator / 360.0; float cutoffRightBottomLeftTop = 1.0; // изменение направления #if _FILL_ORIGIN_BOTTOM cutoffRightBottomLeftTop = _FillClockwise ? 1.75 : 1.25; #elif _FILL_ORIGIN_LEFT cutoffRightBottomLeftTop = 1.5; #elif _FILL_ORIGIN_TOP cutoffRightBottomLeftTop = _FillClockwise ? 1.25 : 1.75; #endif cutoffRightBottomLeftTop += 0.001; float cutoffRotator_ang = cutoffRightBottomLeftTop * -TAU; float2x2 cutoffRotationMatrix = getMatrix(cutoffRotator_ang); float2 cutoffRotator = mul(CommonStartAndSwitcher, cutoffRotationMatrix); float whiteToBlackMask = getMask(getNormalizedAtanMask(cutoffRotator, 0), tRotatorNormalized, 1); // Финальная маска float finalMask = 1.0 - whiteToBlackMask; clip(finalMask - 0.5); /*** Секция для opacity ***/ float oRotatorNormalized = _OpacityRotator / 360.0; float2 oVector = float2(1, -1); float oRightBottomLeftTop = 1.0; int reverseMaskCoords = 0; #if (_FILL_ORIGIN_TOP || _FILL_ORIGIN_BOTTOM) reverseMaskCoords = 1; #endif #if _FILL_ORIGIN_LEFT oVector = float2(-1, 1); #elif _FILL_ORIGIN_TOP oVector = _FillClockwise ? float2(-1, -1) : float2(1, 1); oRightBottomLeftTop = -1.0; #elif _FILL_ORIGIN_BOTTOM oVector = _FillClockwise ? float2(1, 1) : float2(-1, -1); oRightBottomLeftTop = -1.0; #endif float oRotator_ang = oRightBottomLeftTop * (oRotatorNormalized * -TAU); float2x2 oRotationMatrix = getMatrix(oRotator_ang); float2 oRotator = mul(oVector * CommonStartAndSwitcher, oRotationMatrix); float oWhiteToBlackMask = getMask(getNormalizedAtanMask(oRotator, reverseMaskCoords), oRotatorNormalized, 0); // Финальная прозрачность float oFinalMultiply = _MainTex_var.a * max(getNormalizedAtanMask(oRotator, reverseMaskCoords), ceil(oWhiteToBlackMask)); /*** Излучение (Emissive) ***/ // oFinalMultiply чтоб обрезать прозрачную область, где она обрезана в самой текстуре float3 finalColor = _MainTex_var.rgb * _Color.rgb * oFinalMultiply; // Конечный результат (цвет, обработанный маской и повернутый под углом альфа канал) return fixed4(finalColor, oFinalMultiply); } ENDCG } } FallBack "Diffuse" }