UV Texture Coordinates Bounds using Sprite Packer

I’m writing a shader to make a colored glow on Unity UI Images like Fireworks/Photoshop Glow Effect. I found some examples on the internet and I can make it work fine in the Editor, but when I run the game on Android, it does not work. I spend a lot of time figuring what may be the problem and I realized that in the Editor mode the Sprite Packer was disabled and the shader works fine with a single sprite, but when the sprite is packed the shader doesn’t work. The issue must be on those lines of code:

const half2 centerPos = half2(0.5, 0.5);
half dist = max(2*distance(IN.texcoord, centerPos)-shinestart,0);

The idea of the shader is to make a radial effect and it needs the center of the image.The texcoord is relative to the Atlas image, so the centerPos is not the center of the image, but the center of the Atlas itself. Can I have the access to the current sprite’s bounds and center position?

Thanks!

1 Like

I found the solution with help of this thread: "Relative" texcoord when using sprite atlas - Unity Engine - Unity Discussions
You have to send the sprite bounds to the shader using scripting language.
Here’s the shader, if you have any tips to make it better, please, send me.

Shader "Sprites/ShinyDefault"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
       
        _LightColor("LightColor", Color) = (1,1,1,1)
        _ShineWidth("ShineWidth", Range(0,1)) = 0
        _Rect ("Rect Display", Vector) = (0,0,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
    }

    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
        {
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

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

            #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;
                half2 texcoord  : TEXCOORD0;
                float4 worldPosition : TEXCOORD1;
            };
           
            fixed4 _Color;
            fixed4 _LightColor;
            fixed4 _TextureSampleAdd;
            fixed4 _Rect;
            float4 _ClipRect;
            half _ShineWidth;

            v2f vert(appdata_t IN)
            {
                v2f OUT;
                OUT.worldPosition = IN.vertex;
                OUT.vertex = mul(UNITY_MATRIX_MVP, OUT.worldPosition);

                OUT.texcoord = IN.texcoord;
               
                #ifdef UNITY_HALF_TEXEL_OFFSET
                OUT.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1);
                #endif
               
                OUT.color = IN.color * _Color;
                return OUT;
            }

            sampler2D _MainTex;
            float4 _MainTex_TexelSize;
       
           
            fixed4 frag(v2f IN) : SV_Target
            {
               
                const half v = 4;
                half xVal = v*_MainTex_TexelSize.x*_ShineWidth;
                half yVal = v*_MainTex_TexelSize.y*_ShineWidth;
                half2 offX = half2(xVal, yVal);
                half2 offY = half2(xVal, -yVal);
                half colorGlow = (tex2D(_MainTex, clamp(IN.texcoord + offY, _Rect.xy, _Rect.zw))).a +
                                (tex2D(_MainTex, clamp(IN.texcoord - offY, _Rect.xy, _Rect.zw))).a +
                                (tex2D(_MainTex, clamp(IN.texcoord + offX, _Rect.xy, _Rect.zw))).a +
                                (tex2D(_MainTex, clamp(IN.texcoord - offX, _Rect.xy, _Rect.zw))).a;
                const float C = 0.796;
                colorGlow = saturate(2*sin(colorGlow*C));
                half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
                color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
                half2 centerPos = half2((_Rect.x+_Rect.z)/2, (_Rect.y+_Rect.w)/2);
                half maxDist = sqrt(pow((_Rect.z-_Rect.x)/2,2) + pow((_Rect.w-_Rect.y)/2,2));
                half shinestart = (1-_ShineWidth)*maxDist;
                half dist = max(distance(IN.texcoord, centerPos)-shinestart,0);
                half whitePower = color.a*(dist/((max(_ShineWidth,.01)*maxDist)));
                color.rgb = lerp(color.rgb, _LightColor.rgb, whitePower);
                color = lerp(color,_LightColor, colorGlow);
                #ifdef UNITY_UI_ALPHACLIP
                clip (color.a - 0.001);
                #endif
                return color;
            }
        ENDCG
        }
    }
}

And in the scripting language, you have to set the _Rect property. You have to set only when you chance the sprite image:

image = GetComponent<Image> ();
        Sprite sprite = image.sprite;
        Vector4 result = new Vector4(sprite.textureRect.min.x/sprite.texture.width,
            sprite.textureRect.min.y/sprite.texture.height,
            sprite.textureRect.max.x/sprite.texture.width,
            sprite.textureRect.max.y/sprite.texture.height);
        image.material.SetVector ("_Rect", result);

The result:

4 Likes

Thank you so much for this!! This tutorial was exactly what I needed. This was what I used to convert from local uv to atlas uv:

float2 localuv = (i.uv - _Rect.xy) / (_Rect.zw - _Rect.xy);
1 Like