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 Default Shader Modifications

Discussion in 'Shaders' started by unitynoob24, Sep 14, 2021.

  1. unitynoob24

    unitynoob24

    Joined:
    Dec 27, 2014
    Posts:
    398
    Hey guys,

    I am looking to modify the UI Default shader slightly to basically have it so the texture that is passed in could be a texture atlas.

    Then, knowing how many frames total are in the atlas, effectively picking one of the frames at random and using just that image. So changing the UV's of the texture I assume.

    I have done some simple shader stuff in the past, but not really sure where to begin for this. Any tips or suggestions or references would be greatly appreciated. I did a bit of googling earlier today but couldn't find any examples doing exactly what I am looking to do.

    Thanks guys!
     
  2. unitynoob24

    unitynoob24

    Joined:
    Dec 27, 2014
    Posts:
    398
    Update:
    I found a flipbook shader that takes in a texture and treats it as a texture atlas. It works great and plays the entire texture sheet. What I am trying to do now is have it so it picks a single frame each time I instantiate an object that uses a material with this shader. I have it half working, it currently picks a frame but since it is also using time, as time progresses it starts to cycle through more frames. Is it possible to modify what I have here to make it so it picks only one frame per use? Thanks!

    Code (csharp):
    1.  
    2.  
    3. Shader "UnityLibrary/Sprites/FlipBook (Cutout)"
    4. {
    5.    Properties
    6.    {
    7.        [Header(Texture Sheet)]
    8.        _MainTex("Texture", 2D) = "white" {}
    9.        _Cutoff("Alpha Cutoff", Range(0,1)) = 0.15
    10.        [Header(Settings)]
    11.        _ColumnsX("Columns (X)", int) = 1
    12.        _RowsY("Rows (Y)", int) = 1
    13.        _AnimationSpeed("Frames Per Seconds", float) = 10
    14.    }
    15.        SubShader
    16.        {
    17.            Tags {
    18.                "Queue" = "AlphaTest"
    19.                "IgnoreProjector" = "True"
    20.                "PreviewType" = "Plane"
    21.                "RenderType" = "TransparentCutout"
    22.                "DisableBatching" = "True"
    23.            }
    24.  
    25.            LOD 100
    26.  
    27.            Pass
    28.            {
    29.                CGPROGRAM
    30.                #pragma vertex vert
    31.                #pragma fragment frag
    32.                #include "UnityCG.cginc"
    33.  
    34.                struct appdata
    35.                {
    36.                    float4 vertex : POSITION;
    37.                    float2 uv : TEXCOORD0;
    38.                };
    39.  
    40.                struct v2f
    41.                {
    42.                    float2 uv : TEXCOORD0;
    43.                    float4 vertex : SV_POSITION;
    44.                };
    45.  
    46.                float rand(float2 uv)
    47.                {
    48.                    return (frac(dot(uv, float2(12.9898, 78.233) * 2.0)) * 43758.5453);
    49.                }
    50.  
    51.                float _Cutoff;
    52.                sampler2D _MainTex;
    53.                float4 _MainTex_ST;
    54.                uint _ColumnsX;
    55.                uint _RowsY;
    56.                float _AnimationSpeed;
    57.  
    58.                uint Random(int min, int max)  
    59.                {
    60.                    if (min > max)
    61.                        return 1;
    62.  
    63.                    float cap = max - min;
    64.                    int rand = tex2Dlod(_MainTex, _Time.x).r * cap + min;  
    65.                    return rand;
    66.                }
    67.  
    68.                
    69.                v2f vert(appdata v)
    70.                {
    71.                    v2f o;
    72.                    o.vertex = UnityObjectToClipPos(v.vertex);
    73.  
    74.                    // get single sprite size
    75.                    float2 size = float2(1.0f / _ColumnsX, 1.0f / _RowsY);
    76.                    uint totalFrames = _ColumnsX * _RowsY;
    77.  
    78.                    // grab a random index
    79.                    uint index = Random(0, totalFrames - 1);
    80.                    
    81.                    // wrap x and y indexes
    82.                    uint indexX = index % _ColumnsX;
    83.                    uint indexY = floor((index % totalFrames) / _ColumnsX);
    84.  
    85.                    // get offsets to our sprite index
    86.                    float2 offset = float2(size.x*indexX,-size.y*indexY);
    87.  
    88.                    // get single sprite UV
    89.                    float2 newUV = v.uv*size;
    90.  
    91.                    // flip Y (to start 0 from top)
    92.                    newUV.y = newUV.y + size.y*(_RowsY - 1);
    93.  
    94.                    o.uv = newUV + offset;
    95.  
    96.                    return o;
    97.                }
    98.  
    99.                fixed4 frag(v2f i) : SV_Target
    100.                {
    101.                    fixed4 col = tex2D(_MainTex, i.uv);
    102.  
    103.                // cutout
    104.                clip(col.a - _Cutoff);
    105.  
    106.                return col;
    107.            }
    108.        ENDCG
    109.        }
    110.        }
    111. }
    112.  
    113.  
     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    The only way to do what you want is to use a custom script to set the frame index on the Image component's material in an
    OnEnable()
    function. But if you have a script doing that, there's not much point in having a custom shader as you could just as easily have a list of Sprites, or directly point at the atlas, and set the Sprite on the Image component. That'll be significantly more efficient than having each have a unique material.
     
  4. unitynoob24

    unitynoob24

    Joined:
    Dec 27, 2014
    Posts:
    398
    Crap, well thanks anyways! One of the other limitations I am faced with for now is that this needs to be a non c# solution. So I was trying to come up with a way to effectively just change to a random predefined image from a shader, this is super close to working, but like you said I think the only way is to expose the index value on the shader then do a setint on the material :/
     
    Last edited: Sep 15, 2021