Get standard uvs for 9 sliced image shader

I’m woking on UI shaders and noticed that the uvs I get from a simple image as input differ from the ones from a 9-sliced image. This totally makes sense, but I wasn’t expecting it. I’m wondering if there is a way to still get the simple uv data in the shader.
Comparison:

Shader code

Shader "UI/Simple UV"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
    }

    CGINCLUDE
    #include "UnityCG.cginc"
    #include "UnityUI.cginc"
   
    fixed4 _Color;
    fixed4 _TextureSampleAdd;
   
    struct appdata_t
    {
        float4 vertex   : POSITION;
        float4 color    : COLOR;
        float2 texcoord : TEXCOORD0;
    };

    struct v2f
    {
        float4 vertex   : SV_POSITION;
        fixed4 color    : COLOR;
        half2 texcoord  : TEXCOORD0;
    };
   
    v2f vert(appdata_t IN)
    {
        v2f OUT;
        OUT.vertex = UnityObjectToClipPos(IN.vertex);

        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;
    fixed4 frag(v2f IN) : SV_Target
    {
        return (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color * half4(IN.texcoord.xy, 0.0, 1.0);
    }
    ENDCG
   
    SubShader
    {
        Tags
        {
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

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

        Pass
        {
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
        ENDCG
        }
    }
}

I have this problem too, did you ever solve it?
Thanks

Nope, I didn’t need it in the end. But from working quite a bit on ui shaders,I recommend checking out MeshModifiers: https://docs.unity3d.com/ScriptReference/UI.BaseMeshEffect.html
With those you should be easily able to do what you want to do (e.g. put the original uv information in the vertex color).

There’s a simple solution to this that is probably sufficient for most cases. At least it works in mine.

In the vertex shader:

float2 baseUv = float2(0, 0);
baseUv.x = slicedUv.x > 0.5 ? 1 : 0;
baseUv.y = slicedUv.y > 0.5 ? 1 : 0;

This works on the following assumptions:

  1. You’re using this material on a UI element that only has 4 vertices (1 in each corner)
  2. The sliced _MainTex borders do not exceed 50% of the texture size

Unity’s 9 slice UI elements work by actually slicing the mesh into 9 quads, so it’s a mesh with 16 vertices, not 4, hence why this isn’t solvable by applying a shader to a sprite already set to use the built in 9-slice support. It’ll also break on any atlased sprite as their UVs are not necessarily in a 0.0 to 1.0 range, so that 0.5 test won’t work there either.

1 Like

Yeah, as I played around with it more I discovered it’s a truly flimsy solution. :-/
Seems like there should be an easier way to do this…

I guess one way would be to calculate bottom-left, top-right vertex in CS, then set those values on the material and use them to infer the UV in the vertex shader.

As already pointed out, ui elements have a very nice base class with which you can do all kind of magic without the need of setting material properties :slight_smile:

1 Like

Here’s one that works for me
Just make sure in the shader you are looking at vertex color channel Red instead of U coordinates.

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Pool;
using UnityEngine.UI;

public class ImageSliceWithUV : BaseMeshEffect
{

    protected ImageSliceWithUV()
    {}
   
    public override void ModifyMesh(VertexHelper vh)
    {
        List<UIVertex> verts = new List<UIVertex>();
        vh.GetUIVertexStream(verts);

        var orderedVerts = verts.OrderBy(v => v.position.x).ToList();
        var minX = orderedVerts.First().position.x;
        var maxX = orderedVerts.Last().position.x;
                                     
        for (int i = 0; i < verts.Count; i++)
        {
            UIVertex vertex = verts[i];
            Vector3 position = vertex.position;
           
            vertex.color.r = (byte)(Mathf.InverseLerp(minX, maxX, position.x) * 255);
            verts[i] = vertex;
        }

        vh.Clear();
        vh.AddUIVertexTriangleStream(verts);
    }
}
1 Like

@joonturbo
thanks! work for me!