Unity creates curve progress bar
Hello everyone, my name is A Zhao.
When using the Unity engine to make a progress bar, you sometimes encounter a problem. What should you do if the progress bar is not a simple horizontal, vertical or circular shape, but an arbitrary irregular shape? For example:
1. Production method
1. Prepared materials
The principle of this progress bar is very simple. We need a picture that changes grayscale along this path. This picture probably looks like this:
I used the RGB channel as the grayscale here, and the Alpha channel as the display range. Combined together, a picture in Unity will look like this:
Of course, there are other ways to use pictures. For example, if we want to display some gradient colors or textures on the progress bar, we can use the RGB channel of the picture as a texture picture, and then the Alpha channel can be used as a grayscale change. After all, grayscale changes only use One channel is enough, there is no need to use three RGB channels.
2. Calculation process
Purpose:
1. Can be used on the UI
2. Use the image of the Image as input
3. Use the RGB channel of the color of the Image as the color of the progress bar
4. Use the A channel of the color of the Image as the progress control of the progress bar display
The calculation of color is very simple, just get the RGB of the vertex color directly. If you think the pure RGB color doesn’t look good, you can also make another colored picture for display.
Next is the effect of masking the progress bar. Because here I directly used the grayscale gradient of the RGB color of the image, so I first took an RGB grayscale. If, as mentioned above, you want to make a patterned progress bar picture using RGB color, then you can actually use RGB color instead of RGB color for this progress. You can also use the Alpha channel of the picture as grayscale.
No matter what, you get the grayscale value of an image, then enter a value you want to control the progress of, use this value and the grayscale value to do subtraction, and then use the Step function to control the display range. If you want a gradient effect at the transition point, you can use the SmoothStep function. Since grayscale gradients along the path, when the input value changes, the display range will change along the grayscale path.
Finally, use the result of Step as the displayed Alpha value, combined with the RGB color to be displayed, to get the final display color and range of the progress bar.
3. Production of grayscale gradient map
The difficulty of this method is not the writing of the shader, but the production of the materials.
I used a 3D method to create this image. First, I created a patch and set its number of horizontal segments to 1000.
Then expand the UV, the leftmost u coordinate of the patch is 0, and the rightmost u coordinate is 1. Then make a gradient map from left to right. Assign the gradient map to the patch.
Finally, through path deformation, the patch is stretched along the path to obtain the shape.
Finally, after rendering this model, the gradient grayscale image above is obtained.
By doing this, you can barely get a uniformly distributed grayscale image, but it is not 100% accurate. If you want to get a grayscale image that completely matches the change in percentage, for example, draw a scale on the progress bar, and then enter a percentage, and the grayscale of the image just reaches that percentage, then you have to think of other ways to make this image.
3. Shader source code
Shader "azhao/CurveProgress"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {
}
_Color ("Tint", Color) = (1,1,1,1)
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_ColorMask ("Color Mask", Float) = 15
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
_min("min", Range( -1 , 1)) = 0
_max("max", Range( 0 , 1)) = 1
[HideInInspector] _texcoord( "", 2D ) = "white" {
}
}
SubShader
{
LOD 0
Tags {
"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" "CanUseSpriteAtlas"="True" }
Stencil
{
Ref [_Stencil]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
CompFront [_StencilComp]
PassFront [_StencilOp]
FailFront Keep
ZFailFront Keep
CompBack Always
PassBack Keep
FailBack Keep
ZFailBack Keep
}
Cull Off
Lighting Off
ZWrite Off
ZTest [unity_GUIZTestMode]
Blend SrcAlpha OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass
{
Name "Default"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#pragma multi_compile __ UNITY_UI_CLIP_RECT
#pragma multi_compile __ UNITY_UI_ALPHACLIP
#define ASE_NEEDS_FRAG_COLOR
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
half2 texcoord : TEXCOORD0;
float4 worldPosition : TEXCOORD1;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
uniform fixed4 _Color;
uniform fixed4 _TextureSampleAdd;
uniform float4 _ClipRect;
uniform sampler2D _MainTex;
uniform float4 _MainTex_ST;
uniform float _min;
uniform float _max;
v2f vert( appdata_t IN )
{
v2f OUT;
UNITY_SETUP_INSTANCE_ID( IN );
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
UNITY_TRANSFER_INSTANCE_ID(IN, OUT);
OUT.worldPosition = IN.vertex;
OUT.worldPosition.xyz += float3( 0, 0, 0 ) ;
OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
OUT.texcoord = IN.texcoord;
OUT.color = IN.color * _Color;
return OUT;
}
fixed4 frag(v2f IN ) : SV_Target
{
float2 uv_MainTex = IN.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
float4 tex2DNode1 = tex2D( _MainTex, uv_MainTex );
float grayVal = Luminance(tex2DNode1.rgb);
float remapVal = ( grayVal - ( 1.0 - (0.0 + (( IN.color.a + 0.01 ) - 0.0) * (( _max + 1.0 ) - 0.0) / (1.0 - 0.0)) ) );
float smoothstepVal = smoothstep( _min , _max , remapVal);
float4 color = (float4(IN.color.r , IN.color.g , IN.color.b , ( tex2DNode1.a * smoothstepVal )));
#ifdef UNITY_UI_CLIP_RECT
color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
#endif
#ifdef UNITY_UI_ALPHACLIP
clip (color.a - 0.001);
#endif
return color;
}
ENDCG
}
}
}