I want to add a single color background to my texture. In my case, in Unity UI, I can simply create two Image components, one on top of another, and use the one on the bottom as a single color background. However, I would like to do it in a single draw call, using a shader. This is the shader that I use:

   Shader "UI/Background Tinted"
        [PerRendererData]_MainTex ("Sprite Texture", 2D) = "white" {}
        _BgClip ("Offset Mask", Range(0,0.1)) = 0.025

        _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


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

        Cull Off
        Lighting Off
        ZWrite Off
        ZTest [unity_GUIZTestMode]
        Blend SrcAlpha OneMinusSrcAlpha
        ColorMask [_ColorMask]

            Name "Default"
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 2.0

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

            #pragma multi_compile __ UNITY_UI_CLIP_RECT
            #pragma multi_compile __ UNITY_UI_ALPHACLIP

            struct appdata_t
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float2 texcoord : TEXCOORD0;

            struct v2f
                float4 vertex   : SV_POSITION;
                fixed4 color    : COLOR;
                float2 texcoord  : TEXCOORD0;
                float4 worldPosition : TEXCOORD1;

            fixed4 _TextureSampleAdd;
            float4 _ClipRect;
            float _BgClip;

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

                OUT.texcoord = v.texcoord;

                OUT.color = v.color;
                return OUT;

            sampler2D _MainTex;

            fixed4 frag(v2f IN) : SV_Target
                half4 main = tex2D(_MainTex, IN.texcoord);

                float mask = 1.0 - main.a;

                half4 bg = IN.color;
                bg.a = mask;

                half4 color = main + (bg * mask);

                return color;

With two Images component everything is fine. However, with single shader approach I encounter sharp edges near image alpha boundaries.

Using 2 images, looking fine: two images

Using single shader, jagged edges: with shader

How do i fix that, using a single shader approach?

| improve this question | | | | |
  • \$\begingroup\$ You forgot to dim down the main texture where it's fading out, Both main and bg should be modulated by alpha — main fading out, and bg fading in as the alpha drops. \$\endgroup\$ – DMGregory Oct 4 '19 at 11:53

I figured it out using the following code in pixel shader:

            float mask = 1.0 - main.a;

            half4 bg = IN.color;
            bg.a = mask;

            half4 color = half4((main.rgb * main.a + bg.rgb * bg.a), main.a + bg.a);

            return color;
| improve this answer | | | | |
  • \$\begingroup\$ You can also do this using lerp(bg, main, main.a) \$\endgroup\$ – DMGregory Oct 4 '19 at 11:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.