Search Unity

Get standard uvs for 9 sliced image shader

Discussion in 'Shaders' started by Johannski, Jul 12, 2017.

  1. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    826
    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:


    Code (CSharp):
    1. Shader "UI/Simple UV"
    2. {
    3.     Properties
    4.     {
    5.         [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
    6.         _Color ("Tint", Color) = (1,1,1,1)
    7.     }
    8.  
    9.     CGINCLUDE
    10.     #include "UnityCG.cginc"
    11.     #include "UnityUI.cginc"
    12.    
    13.     fixed4 _Color;
    14.     fixed4 _TextureSampleAdd;
    15.    
    16.     struct appdata_t
    17.     {
    18.         float4 vertex   : POSITION;
    19.         float4 color    : COLOR;
    20.         float2 texcoord : TEXCOORD0;
    21.     };
    22.  
    23.     struct v2f
    24.     {
    25.         float4 vertex   : SV_POSITION;
    26.         fixed4 color    : COLOR;
    27.         half2 texcoord  : TEXCOORD0;
    28.     };
    29.    
    30.     v2f vert(appdata_t IN)
    31.     {
    32.         v2f OUT;
    33.         OUT.vertex = UnityObjectToClipPos(IN.vertex);
    34.  
    35.         OUT.texcoord = IN.texcoord;
    36.        
    37.         #ifdef UNITY_HALF_TEXEL_OFFSET
    38.         OUT.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1);
    39.         #endif
    40.        
    41.         OUT.color = IN.color * _Color;
    42.         return OUT;
    43.     }
    44.  
    45.     sampler2D _MainTex;
    46.     fixed4 frag(v2f IN) : SV_Target
    47.     {
    48.         return (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color * half4(IN.texcoord.xy, 0.0, 1.0);
    49.     }
    50.     ENDCG
    51.    
    52.     SubShader
    53.     {
    54.         Tags
    55.         {
    56.             "Queue"="Transparent"
    57.             "IgnoreProjector"="True"
    58.             "RenderType"="Transparent"
    59.             "PreviewType"="Plane"
    60.             "CanUseSpriteAtlas"="True"
    61.         }
    62.  
    63.         Cull Off
    64.         Lighting Off
    65.         ZWrite Off
    66.         ZTest [unity_GUIZTestMode]
    67.         Blend SrcAlpha OneMinusSrcAlpha
    68.  
    69.         Pass
    70.         {
    71.         CGPROGRAM
    72.             #pragma vertex vert
    73.             #pragma fragment frag
    74.         ENDCG
    75.         }
    76.     }
    77. }
     
  2. ricsacwhatwapp

    ricsacwhatwapp

    Joined:
    Nov 9, 2016
    Posts:
    5
    I have this problem too, did you ever solve it?
    Thanks
     
  3. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    826
    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).
     
    Last edited: Jul 26, 2019
  4. lloydv

    lloydv

    Joined:
    Sep 15, 2015
    Posts:
    55
    There's a simple solution to this that is probably sufficient for most cases. At least it works in mine.

    In the vertex shader:
    Code (CSharp):
    1. float2 baseUv = float2(0, 0);
    2. baseUv.x = slicedUv.x > 0.5 ? 1 : 0;
    3. 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
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    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.
     
    SamFernGamer4k likes this.
  6. lloydv

    lloydv

    Joined:
    Sep 15, 2015
    Posts:
    55
    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.
     
  7. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    826
    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 :)

     
    Plomber likes this.
  8. joonturbo

    joonturbo

    Joined:
    Jun 24, 2013
    Posts:
    77
    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.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using UnityEngine;
    5. using UnityEngine.Pool;
    6. using UnityEngine.UI;
    7.  
    8. public class ImageSliceWithUV : BaseMeshEffect
    9. {
    10.  
    11.     protected ImageSliceWithUV()
    12.     {}
    13.    
    14.     public override void ModifyMesh(VertexHelper vh)
    15.     {
    16.         List<UIVertex> verts = new List<UIVertex>();
    17.         vh.GetUIVertexStream(verts);
    18.  
    19.         var orderedVerts = verts.OrderBy(v => v.position.x).ToList();
    20.         var minX = orderedVerts.First().position.x;
    21.         var maxX = orderedVerts.Last().position.x;
    22.                                      
    23.         for (int i = 0; i < verts.Count; i++)
    24.         {
    25.             UIVertex vertex = verts[i];
    26.             Vector3 position = vertex.position;
    27.            
    28.             vertex.color.r = (byte)(Mathf.InverseLerp(minX, maxX, position.x) * 255);
    29.             verts[i] = vertex;
    30.         }
    31.  
    32.         vh.Clear();
    33.         vh.AddUIVertexTriangleStream(verts);
    34.     }
    35. }
    36.