Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

UI-Defaut.shader. How to get Sprite position and size?

Discussion in 'Shaders' started by vladstorm_, Jan 14, 2016.

  1. vladstorm_

    vladstorm_

    Joined:
    Nov 26, 2013
    Posts:
    184
    So, I spent 2 days trying to get tile(Sprite in Texture with multiple Sprites) position and size in UI-Defaut.shader?
    Please, tell me how can I do it.
    It's so not obvious.

    inside
    Code (CSharp):
    1. half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
    the IN.texcoord only passes insides of a Sprite in the texture. but how can I get the borders of the particular Sprite inside the texture?
     
  2. vladstorm_

    vladstorm_

    Joined:
    Nov 26, 2013
    Posts:
    184
    In other words. I'm modifying UI-Defaut.shader for the UI.Image component.
    How can I get Sprite uv offset and uv scale values in the shader?

    I heard there is float4 _MainTex_ST but i still can't figure out what exactly is stored there.
     
  3. vladstorm_

    vladstorm_

    Joined:
    Nov 26, 2013
    Posts:
    184
    So, this is the UI-Default.shader
    Code (CSharp):
    1. Shader "UI/Default"
    2. {
    3.     Properties
    4.     {
    5.         [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
    6.         _Color ("Tint", Color) = (1,1,1,1)
    7.    
    8.         _StencilComp ("Stencil Comparison", Float) = 8
    9.         _Stencil ("Stencil ID", Float) = 0
    10.         _StencilOp ("Stencil Operation", Float) = 0
    11.         _StencilWriteMask ("Stencil Write Mask", Float) = 255
    12.         _StencilReadMask ("Stencil Read Mask", Float) = 255
    13.  
    14.         _ColorMask ("Color Mask", Float) = 15
    15.  
    16.         [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
    17.     }
    18.  
    19.     SubShader
    20.     {
    21.         Tags
    22.         {
    23.             "Queue"="Transparent"
    24.             "IgnoreProjector"="True"
    25.             "RenderType"="Transparent"
    26.             "PreviewType"="Plane"
    27.             "CanUseSpriteAtlas"="True"
    28.         }
    29.    
    30.         Stencil
    31.         {
    32.             Ref [_Stencil]
    33.             Comp [_StencilComp]
    34.             Pass [_StencilOp]
    35.             ReadMask [_StencilReadMask]
    36.             WriteMask [_StencilWriteMask]
    37.         }
    38.  
    39.         Cull Off
    40.         Lighting Off
    41.         ZWrite Off
    42.         ZTest [unity_GUIZTestMode]
    43.         Blend SrcAlpha OneMinusSrcAlpha
    44.         ColorMask [_ColorMask]
    45.  
    46.         Pass
    47.         {
    48.         CGPROGRAM
    49.             #pragma vertex vert
    50.             #pragma fragment frag
    51.  
    52.             #include "UnityCG.cginc"
    53.             #include "UnityUI.cginc"
    54.  
    55.             #pragma multi_compile __ UNITY_UI_ALPHACLIP
    56.        
    57.             struct appdata_t
    58.             {
    59.                 float4 vertex   : POSITION;
    60.                 float4 color    : COLOR;
    61.                 float2 texcoord : TEXCOORD0;
    62.             };
    63.  
    64.             struct v2f
    65.             {
    66.                 float4 vertex   : SV_POSITION;
    67.                 fixed4 color    : COLOR;
    68.                 half2 texcoord  : TEXCOORD0;
    69.                 float4 worldPosition : TEXCOORD1;
    70.             };
    71.        
    72.             fixed4 _Color;
    73.             fixed4 _TextureSampleAdd;
    74.             float4 _ClipRect;
    75.  
    76.             v2f vert(appdata_t IN)
    77.             {
    78.                 v2f OUT;
    79.                 OUT.worldPosition = IN.vertex;
    80.                 OUT.vertex = mul(UNITY_MATRIX_MVP, OUT.worldPosition);
    81.  
    82.                 OUT.texcoord = IN.texcoord;
    83.            
    84.                 #ifdef UNITY_HALF_TEXEL_OFFSET
    85.                 OUT.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1);
    86.                 #endif
    87.            
    88.                 OUT.color = IN.color * _Color;
    89.                 return OUT;
    90.             }
    91.  
    92.             sampler2D _MainTex;
    93.             float4 _MainTex_TexelSize; //i added
    94.             float4 _MainTex_ST; //i added
    95.  
    96.             fixed4 frag(v2f IN) : SV_Target
    97.             {
    98.                 half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
    99.            
    100.                 color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
    101.            
    102.                 #ifdef UNITY_UI_ALPHACLIP
    103.                 clip (color.a - 0.001);
    104.                 #endif
    105.  
    106.                 return color;
    107.             }
    108.         ENDCG
    109.         }
    110.     }
    111. }
    112.  
    from the documentation float4 _MainTex_TexelSize.xy should give me the 1px in UV space.
    it means that this code should move texture by exactly 1px.
    Code (CSharp):
    1. float2 uv = IN.texcoord + fixed2(1.0, 1.0)*_MainTex_TexelSize.xy;
    2. half4 color = tex2D(_MainTex, uv);
    but in fact it doesn't. So, what is exactly stored in _MainTex_TexelSize? and how can I get the Sprite position and size then?

    Moreover this also doesn't move sprite by exactly 1px
    Code (CSharp):
    1. float2 uv = IN.texcoord + fixed2(1.0, 1.0)*1.0/(_ScreenParams.xy);
    2. half4 color = tex2D(_MainTex, uv);
    And this code doesn't move the offset to (0,0)
    Code (CSharp):
    1. float2 uv = IN.texcoord - _MainTex_ST.zw;
    2. half4 color = tex2D(_MainTex, uv);
    Guys, I spent like 18 hours already figuring it out and digged thru so many code examples and still it doesn't work and i didn't find the solution.
     
    Last edited: Jan 15, 2016
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    In a normal material _ST stores the offset and scale parameters for the texture and _TexelSize stores the reciprocal of the pixel dimensions and the pixel dimensions.

    UI and sprites are not normal. Each quad is created with the UVs directly instead of as material properties so they can be drawn if the fewest possible draw calls. There's no way* to get the data you want as each vertex only knows about itself and not the vertices around it.

    * There a hacks you can do using geometry shaders to get access to 3 vertices at a time, but this is slow.
     
  5. vladstorm_

    vladstorm_

    Joined:
    Nov 26, 2013
    Posts:
    184
    so you are saying in unity its impossible to get tile position and size of a sprite in a default sprite shader for multiple sprite texture. but this is ridiculous.

    so, i can't even write simple sprite effects. like simple 2d sprite effects with knowing the beginning and the end of the sprite inside the shader. how come?

    ok. tell me the hack. maybe at least i can set params for each sprite i need manually.
    i just need to know how. coz i dont know where IN.texcoord.xy starts and ends within the sprite inside the shader.
    how i can possible calculate those values? even outside the shader? i would just pass them to the shader.
    all i need to know is how to calculate them. it works in my case.
     
  6. vladstorm_

    vladstorm_

    Joined:
    Nov 26, 2013
    Posts:
    184
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    I don't do much with sprites, I just know the limitations of the rendering system. You can absolutely take a sprite and give it a unique material with any kind of information you want, but that would mean it's a unique draw call as would be any others you have unique properties for. The point of the system is to have the fewest draw calls possible on hardware that doesn't support instancing. My understanding is for animations people generally drive it via script rather than in shader, or use a quad with a custom material outside the sprite system. There's probably a couple of assets in the store to help with stuff like this.
     
  8. vladstorm_

    vladstorm_

    Joined:
    Nov 26, 2013
    Posts:
    184
    i'd love to use multisprite system in unity and keep all my sprites in one sprite sheet and then write a shader for a single sprite. it sounds like a normal way. its strange that now i have to find way around coz i can't get tile information inside the shader.
    maybe i can check some assets on asset store
    but i already googled pretty a lot and didnt find any examples where people actually calculated sprite tile-int information inside the sprite shader.
    i believe it shouldnt be like this. i hope someone from unity can tell me how to calculate it.
    i could do a lot of cool effects and put them to the asset store but i just cant atm coz i dunno how it works.
     
  9. Democide

    Democide

    Joined:
    Jan 29, 2013
    Posts:
    315
    sorry, if I'm bumping this, but I'm having a similar problem.

    When I have a shader that uses the vertex position to generate the colors for a 3d object, it uses the vertex positions independent of the object position, so when the object moves, the colors move with the object. But when I use the same calculations in a UI shader, then the colors are stuck in place, as the object moves.

    Now I want the first behavior on a UI shader. Anyone have any ideas?
     
  10. GamerST

    GamerST

    Joined:
    Oct 14, 2013
    Posts:
    4
    @Martin Sharkbomb
    I have the same issue with unity 5.4.
    and I found this
    I'm pretty new in shader world, Any idea is welcome.
     
  11. DmitryAndreevMel

    DmitryAndreevMel

    Joined:
    Mar 7, 2017
    Posts:
    14
    There is a workaround: you can use
    https://docs.unity3d.com/ScriptReference/UI.IMeshModifier.html
    to modify mesh generated by the UI system. Here you can get positions of the vertices of entire mesh (quad for unpacked sprites) and calculate parameters you need and store them in your newly-generated mesh, for example in UV1 channel, which you can later access in a shader.
    You also won't break UI batching. The only drawback is each mesh update with MeshModifier generates some garbage (original mesh) and takes additional CPU time, so be careful with per-frame updates (if you use your sprite like background then move it like all static UI elements to nested canvas to avoid mesh recalculations each time smth changed in your UI)