[WIKI] Drawing an UI element using UnityEngine.Gradient

I needed a way to draw some UI using a gradient, like a progress bar for instance:

Sharing this with you guys,

  • there’s a custom editor for the shader
  • works exactly like UnityEngine.Gradient does (8 colors max)
  • it’s a 1:1 copy of Unity UI Default shader + gradient added
  • it only does blend currently but it’s probably what most people are looking for anyway

Easier said than done (damn float1 silent cast :rage:) but it’s finally here :).

Part 1:

using JetBrains.Annotations;
using UnityEditor;
using UnityEngine;

// ReSharper disable once CheckNamespace

[UsedImplicitly]
public class HiddenDrawer : MaterialPropertyDrawer
{
    public override void OnGUI(Rect position, MaterialProperty prop, string label, MaterialEditor editor)
    {
    }

    public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
    {
        return -EditorGUIUtility.standardVerticalSpacing;
    }
}

Part 2:

using System;
using System.Linq;
using JetBrains.Annotations;
using UnityEditor;
using UnityEngine;

// ReSharper disable once CheckNamespace
// ReSharper disable once InconsistentNaming
[UsedImplicitly]
public class UIGradientShaderGUI : ShaderGUI
{
    public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
    {
        base.OnGUI(materialEditor, properties);
     
        EditorGUILayout.Space();

        var material = materialEditor.target as Material;
        if (material == null)
            throw new ArgumentNullException(nameof(material));

        var colorKeys = Enumerable
            .Range(0, material.GetInt("_Colors"))
            .Select((s, t) => new GradientColorKey(material.GetColor($"_Color{t}"), material.GetFloat($"_ColorTime{t}")))
            .ToArray();

        var alphaKeys = Enumerable
            .Range(0, material.GetInt("_Alphas"))
            .Select((s, t) => new GradientAlphaKey(material.GetFloat($"_Alpha{t}"), material.GetFloat($"_AlphaTime{t}")))
            .ToArray();

        var gradient = new Gradient();
        gradient.SetKeys(colorKeys, alphaKeys);

        EditorGUI.BeginChangeCheck();

        var field = EditorGUILayout.GradientField("Gradient", gradient);

        if (!EditorGUI.EndChangeCheck())
            return;

        for (var i = 0; i < 8; i++)
        {
            material.SetColor($"_Color{i}", Color.magenta);
            material.SetFloat($"_ColorTime{i}", -1.0f);
            material.SetFloat($"_Alpha{i}", -1.0f);
            material.SetFloat($"_AlphaTime{i}", -1.0f);
        }

        for (var i = 0; i < field.colorKeys.Length; i++)
        {
            material.SetColor($"_Color{i}", field.colorKeys[i].color);
            material.SetFloat($"_ColorTime{i}", field.colorKeys[i].time);
        }

        for (var i = 0; i < field.alphaKeys.Length; i++)
        {
            material.SetFloat($"_Alpha{i}", field.alphaKeys[i].alpha);
            material.SetFloat($"_AlphaTime{i}", field.alphaKeys[i].time);
        }

        material.SetInt("_Colors", field.colorKeys.Length);

        material.SetInt("_Alphas", field.alphaKeys.Length);

        EditorUtility.SetDirty(material);
    }
}

Part 3:

// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)

Shader "UI Gradient"
{
    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
     
        [Hidden] _Color0 ("Color 0", Color) = (1, 1, 1, 1)
        [Hidden] _Color1 ("Color 1", Color) = (1, 1, 1, 1)
        [Hidden] _Color2 ("Color 2", Color) = (1, 1, 1, 1)
        [Hidden] _Color3 ("Color 3", Color) = (1, 1, 1, 1)
        [Hidden] _Color4 ("Color 4", Color) = (1, 1, 1, 1)
        [Hidden] _Color5 ("Color 5", Color) = (1, 1, 1, 1)
        [Hidden] _Color6 ("Color 6", Color) = (1, 1, 1, 1)
        [Hidden] _Color7 ("Color 7", Color) = (1, 1, 1, 1)
     
        [Hidden] _ColorTime0 ("Color Time 0", Float) = 0
        [Hidden] _ColorTime1 ("Color Time 1", Float) = 0
        [Hidden] _ColorTime2 ("Color Time 2", Float) = 0
        [Hidden] _ColorTime3 ("Color Time 3", Float) = 0
        [Hidden] _ColorTime4 ("Color Time 4", Float) = 0
        [Hidden] _ColorTime5 ("Color Time 5", Float) = 0
        [Hidden] _ColorTime6 ("Color Time 6", Float) = 0
        [Hidden] _ColorTime7 ("Color Time 7", Float) = 0

        [Hidden] _Colors ("Colors", Int) = 0
         
        [Hidden] _Alpha0 ("Alpha 0", Float) = 0
        [Hidden] _Alpha1 ("Alpha 1", Float) = 0
        [Hidden] _Alpha2 ("Alpha 2", Float) = 0
        [Hidden] _Alpha3 ("Alpha 3", Float) = 0
        [Hidden] _Alpha4 ("Alpha 4", Float) = 0
        [Hidden] _Alpha5 ("Alpha 5", Float) = 0
        [Hidden] _Alpha6 ("Alpha 6", Float) = 0
        [Hidden] _Alpha7 ("Alpha 7", Float) = 0

        [Hidden] _AlphaTime0 ("Alpha Time 0", Float) = 0
        [Hidden] _AlphaTime1 ("Alpha Time 1", Float) = 0
        [Hidden] _AlphaTime2 ("Alpha Time 2", Float) = 0
        [Hidden] _AlphaTime3 ("Alpha Time 3", Float) = 0
        [Hidden] _AlphaTime4 ("Alpha Time 4", Float) = 0
        [Hidden] _AlphaTime5 ("Alpha Time 5", Float) = 0
        [Hidden] _AlphaTime6 ("Alpha Time 6", Float) = 0
        [Hidden] _AlphaTime7 ("Alpha Time 7", Float) = 0

        [Hidden] _Alphas ("Alphas", Int) = 0
    }

    SubShader
    {
        Tags
        {
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Stencil
        {
            Ref [_Stencil]
            Comp [_StencilComp]
            Pass [_StencilOp]
            ReadMask [_StencilReadMask]
            WriteMask [_StencilWriteMask]
        }

        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 2.0

            #include "UnityCG.cginc"
            #include "UnityUI.cginc"

            #pragma multi_compile_local _ UNITY_UI_CLIP_RECT
            #pragma multi_compile_local _ UNITY_UI_ALPHACLIP

            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;
                float2 texcoord  : TEXCOORD0;
                float4 worldPosition : TEXCOORD1;
                UNITY_VERTEX_OUTPUT_STEREO
            };

            sampler2D _MainTex;
            fixed4 _Color;
            fixed4 _TextureSampleAdd;
            float4 _ClipRect;
            float4 _MainTex_ST;

            float4 _Color0, _Color1, _Color2, _Color3, _Color4, _Color5, _Color6, _Color7;
            float  _Alpha0, _Alpha1, _Alpha2, _Alpha3, _Alpha4, _Alpha5, _Alpha6, _Alpha7;
            float  _ColorTime0, _ColorTime1, _ColorTime2, _ColorTime3, _ColorTime4, _ColorTime5, _ColorTime6, _ColorTime7;
            float  _AlphaTime0, _AlphaTime1, _AlphaTime2, _AlphaTime3, _AlphaTime4, _AlphaTime5, _AlphaTime6, _AlphaTime7;
            int _Colors, _Alphas;

            float4 get_gradient(float2 texcoord)
            {
                float4 cc[8] = { _Color0, _Color1, _Color2, _Color3, _Color4, _Color5, _Color6, _Color7 };
                float  aa[8] = { _Alpha0, _Alpha1, _Alpha2, _Alpha3, _Alpha4, _Alpha5, _Alpha6, _Alpha7 };
                float  ct[8] = { _ColorTime0, _ColorTime1, _ColorTime2, _ColorTime3, _ColorTime4, _ColorTime5, _ColorTime6, _ColorTime7 };
                float  at[8] = { _AlphaTime0, _AlphaTime1, _AlphaTime2, _AlphaTime3, _AlphaTime4, _AlphaTime5, _AlphaTime6, _AlphaTime7 };

                float4 cv1 = cc[0], cv2 = cc[_Colors - 1];
                float  ct1 = ct[0], ct2 = ct[_Colors - 1];
                float  av1 = aa[0], av2 = aa[_Alphas - 1];
                float  at1 = at[0], at2 = at[_Alphas - 1];

                float t = texcoord.x;

                for (int i = 0; i < _Colors; i++)
                {
                    if (ct[i] > t)
                        break;

                    cv1 = cc[i];
                    ct1 = ct[i];
                }

                for (int j = 0; j < _Colors; j++)
                {
                    if (ct[j] < t)
                        continue;

                    cv2 = cc[j];
                    ct2 = ct[j];
                    break;
                }

                for (int k = 0; k < _Alphas; k++)
                {
                    if (at[k] > t)
                        break;

                    av1 = aa[k];
                    at1 = at[k];
                }

                for (int l = 0; l < _Alphas; l++)
                {
                    if (at[l] < t)
                        continue;

                    av2 = aa[l];
                    at2 = at[l];
                    break;
                }

                float  lerpA = (t - at1) / (at2 - at1);
                float  lerpC = (t - ct1) / (ct2 - ct1);
                float4 finalC = lerp(cv1, cv2, lerpC);
                float4 finalA = lerp(av1, av2, lerpA);
                finalC.a = finalA;

                return finalC;
            }

            v2f vert(appdata_t v)
            {
                v2f OUT;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
                OUT.worldPosition = v.vertex;
                OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);

                OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
                OUT.color = v.color * _Color;

                return OUT;
            }

            fixed4 frag(v2f IN) : SV_Target
            {
                half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;

                #ifdef UNITY_UI_CLIP_RECT
                color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
                #endif

                #ifdef UNITY_UI_ALPHACLIP
                clip (color.a - 0.001);
                #endif

                float2 texcoord = (IN.texcoord - _MainTex_ST.zw) / _MainTex_ST.xy;
                float4 gradient = get_gradient(texcoord);
                return color * gradient;
            }
        ENDCG
        }
    }
    CustomEditor "UIGradientShaderGUI"
}

Enjoy and please share your improvements :).

6 Likes

I know this is way late, but this is the only gradient maker i can find that works anywhere, and i can get it to work from the editor, but how do i create gradients from code??

        material = new Material(shader);

        material.SetColor("Color 0", color1);
        material.SetFloat("Color Time 0", 0);
        material.SetColor("Color 1", color2);
        material.SetFloat("Color Time 1", 1);

I assumed that this would work

A rough guess is that you have to use “_Color0” and so on … shader variable name, not description.

Can it be used for vertical gradient in UI Images? (Avoiding to rotate the actual image?)

With vertical flip toogle

// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
Shader "UI Gradient"
{
    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)] _IsVertical ("Is vertical", Float) = 0
        [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
    
        [Hidden] _Color0 ("Color 0", Color) = (1, 1, 1, 1)
        [Hidden] _Color1 ("Color 1", Color) = (1, 1, 1, 1)
        [Hidden] _Color2 ("Color 2", Color) = (1, 1, 1, 1)
        [Hidden] _Color3 ("Color 3", Color) = (1, 1, 1, 1)
        [Hidden] _Color4 ("Color 4", Color) = (1, 1, 1, 1)
        [Hidden] _Color5 ("Color 5", Color) = (1, 1, 1, 1)
        [Hidden] _Color6 ("Color 6", Color) = (1, 1, 1, 1)
        [Hidden] _Color7 ("Color 7", Color) = (1, 1, 1, 1)
    
        [Hidden] _ColorTime0 ("Color Time 0", Float) = 0
        [Hidden] _ColorTime1 ("Color Time 1", Float) = 0
        [Hidden] _ColorTime2 ("Color Time 2", Float) = 0
        [Hidden] _ColorTime3 ("Color Time 3", Float) = 0
        [Hidden] _ColorTime4 ("Color Time 4", Float) = 0
        [Hidden] _ColorTime5 ("Color Time 5", Float) = 0
        [Hidden] _ColorTime6 ("Color Time 6", Float) = 0
        [Hidden] _ColorTime7 ("Color Time 7", Float) = 0
        [Hidden] _Colors ("Colors", Int) = 0
        
        [Hidden] _Alpha0 ("Alpha 0", Float) = 0
        [Hidden] _Alpha1 ("Alpha 1", Float) = 0
        [Hidden] _Alpha2 ("Alpha 2", Float) = 0
        [Hidden] _Alpha3 ("Alpha 3", Float) = 0
        [Hidden] _Alpha4 ("Alpha 4", Float) = 0
        [Hidden] _Alpha5 ("Alpha 5", Float) = 0
        [Hidden] _Alpha6 ("Alpha 6", Float) = 0
        [Hidden] _Alpha7 ("Alpha 7", Float) = 0
        [Hidden] _AlphaTime0 ("Alpha Time 0", Float) = 0
        [Hidden] _AlphaTime1 ("Alpha Time 1", Float) = 0
        [Hidden] _AlphaTime2 ("Alpha Time 2", Float) = 0
        [Hidden] _AlphaTime3 ("Alpha Time 3", Float) = 0
        [Hidden] _AlphaTime4 ("Alpha Time 4", Float) = 0
        [Hidden] _AlphaTime5 ("Alpha Time 5", Float) = 0
        [Hidden] _AlphaTime6 ("Alpha Time 6", Float) = 0
        [Hidden] _AlphaTime7 ("Alpha Time 7", Float) = 0
        [Hidden] _Alphas ("Alphas", Int) = 0
    }
    SubShader
    {
        Tags
        {
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }
        Stencil
        {
            Ref [_Stencil]
            Comp [_StencilComp]
            Pass [_StencilOp]
            ReadMask [_StencilReadMask]
            WriteMask [_StencilWriteMask]
        }
        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 2.0
            #include "UnityCG.cginc"
            #include "UnityUI.cginc"
            #pragma multi_compile_local _ UNITY_UI_CLIP_RECT
            #pragma multi_compile_local _ UNITY_UI_ALPHACLIP
            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;
                float2 texcoord  : TEXCOORD0;
                float4 worldPosition : TEXCOORD1;
                UNITY_VERTEX_OUTPUT_STEREO
            };
            sampler2D _MainTex;
            fixed4 _Color;
            fixed4 _TextureSampleAdd;
            float4 _ClipRect;
            float4 _MainTex_ST;
            float _IsVertical;
            float4 _Color0, _Color1, _Color2, _Color3, _Color4, _Color5, _Color6, _Color7;
            float  _Alpha0, _Alpha1, _Alpha2, _Alpha3, _Alpha4, _Alpha5, _Alpha6, _Alpha7;
            float  _ColorTime0, _ColorTime1, _ColorTime2, _ColorTime3, _ColorTime4, _ColorTime5, _ColorTime6, _ColorTime7;
            float  _AlphaTime0, _AlphaTime1, _AlphaTime2, _AlphaTime3, _AlphaTime4, _AlphaTime5, _AlphaTime6, _AlphaTime7;
            int _Colors, _Alphas;
            float4 get_gradient(float2 texcoord)
            {
                float4 cc[8] = { _Color0, _Color1, _Color2, _Color3, _Color4, _Color5, _Color6, _Color7 };
                float  aa[8] = { _Alpha0, _Alpha1, _Alpha2, _Alpha3, _Alpha4, _Alpha5, _Alpha6, _Alpha7 };
                float  ct[8] = { _ColorTime0, _ColorTime1, _ColorTime2, _ColorTime3, _ColorTime4, _ColorTime5, _ColorTime6, _ColorTime7 };
                float  at[8] = { _AlphaTime0, _AlphaTime1, _AlphaTime2, _AlphaTime3, _AlphaTime4, _AlphaTime5, _AlphaTime6, _AlphaTime7 };
                float4 cv1 = cc[0], cv2 = cc[_Colors - 1];
                float  ct1 = ct[0], ct2 = ct[_Colors - 1];
                float  av1 = aa[0], av2 = aa[_Alphas - 1];
                float  at1 = at[0], at2 = at[_Alphas - 1];
                float t = _IsVertical ? texcoord.y : texcoord.x;
                for (int i = 0; i < _Colors; i++)
                {
                    if (ct[i] > t)
                        break;
                    cv1 = cc[i];
                    ct1 = ct[i];
                }
                for (int j = 0; j < _Colors; j++)
                {
                    if (ct[j] < t)
                        continue;
                    cv2 = cc[j];
                    ct2 = ct[j];
                    break;
                }
                for (int k = 0; k < _Alphas; k++)
                {
                    if (at[k] > t)
                        break;
                    av1 = aa[k];
                    at1 = at[k];
                }
                for (int l = 0; l < _Alphas; l++)
                {
                    if (at[l] < t)
                        continue;
                    av2 = aa[l];
                    at2 = at[l];
                    break;
                }
                float  lerpA = (t - at1) / (at2 - at1);
                float  lerpC = (t - ct1) / (ct2 - ct1);
                float4 finalC = lerp(cv1, cv2, lerpC);
                float4 finalA = lerp(av1, av2, lerpA);
                finalC.a = finalA;
                return finalC;
            }
            v2f vert(appdata_t v)
            {
                v2f OUT;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
                OUT.worldPosition = v.vertex;
                OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
                OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
                OUT.color = v.color * _Color;
                return OUT;
            }
            fixed4 frag(v2f IN) : SV_Target
            {
                half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
                #ifdef UNITY_UI_CLIP_RECT
                color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
                #endif
                #ifdef UNITY_UI_ALPHACLIP
                clip (color.a - 0.001);
                #endif
                float2 inTex = IN.texcoord;
                float2 texcoord = (inTex - _MainTex_ST.zw) / _MainTex_ST.xy;
                float4 gradient = get_gradient(texcoord);
                return color * gradient;
            }
        ENDCG
        }
    }
    CustomEditor "UIGradientShaderGUI"
}
2 Likes

Thank you for this!! It helped me a lot!
I’m having trouble when I compile my project simply as PC Standalone. I believe the errors I’m getting are due to the “using UnityEditor;” in the scripts. Would anyone know how can i solve this problem?