Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Packing and Unpacking Colors for Batched Per Sprite Effects

Discussion in 'Shaders' started by Mikeysee, Nov 13, 2014.

  1. Mikeysee

    Mikeysee

    Joined:
    Oct 14, 2013
    Posts:
    155
    Hi,

    So I have had the idea that I can write a custom shader for the built-in SpriteRenderer that lets me do per-sprite based effects without breaking batching because its per-vertex data that is being passed in.

    What I would like to do is pack the sprite color into a single float then set that in a single channel of the color leaving me with the other 3 channels for whatever I want.

    So this is what I have on the C# side:

    Code (CSharp):
    1. [ExecuteInEditMode]
    2. public class SpriteEffects : MonoBehaviour
    3. {
    4.     [Range(0, 2)]
    5.     public float brightnessAmount = 1;
    6.    
    7.     [Range(0, 2)]
    8.     public float greyscaleAmount = 1;
    9.    
    10.     public Color tint;
    11.    
    12.     private SpriteRenderer spriteRenderer;
    13.    
    14.     void Start()
    15.     {
    16.         spriteRenderer = GetComponent<SpriteRenderer>();
    17.     }
    18.  
    19.     public void Update()
    20.     {
    21.         spriteRenderer.color = GetColor(tint, brightnessAmount, greyscaleAmount, 1f);
    22.     }
    23.  
    24.     public Color GetColor(Color color, float brightness, float greyscale, float somethingelse)
    25.     {
    26.         // Pack the color into a single float
    27.         var magic = new Vector4(1.0f, 1/255.0f, 1/65025.0f, 1/160581375.0f);
    28.         var v4 = new Vector4(color.r, color.g, color.b, color.a);
    29.         var packedColor = Vector4.Dot(v4, magic);
    30.  
    31.         return new Color(packedColor, brightness, greyscale, somethingelse);
    32.     }
    33. }
    Then on the shader side:

    Code (CSharp):
    1. Shader "Sprites/Little Lords"
    2. {
    3.     Properties
    4.     {
    5.         [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
    6.         [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
    7.     }
    8.     SubShader
    9.     {
    10.         Tags
    11.         {
    12.             "Queue"="Transparent"
    13.             "IgnoreProjector"="True"
    14.             "RenderType"="Transparent"
    15.             "PreviewType"="Plane"
    16.             "CanUseSpriteAtlas"="True"
    17.         }
    18.         Cull Off
    19.         Lighting Off
    20.         ZWrite Off
    21.         Fog { Mode Off }
    22.         Blend SrcAlpha OneMinusSrcAlpha
    23.         Pass
    24.         {
    25.         CGPROGRAM
    26.             #pragma vertex vert
    27.             #pragma fragment frag
    28.             #pragma multi_compile DUMMY PIXELSNAP_ON
    29.             #include "UnityCG.cginc"
    30.          
    31.             struct appdata_t
    32.             {
    33.                 float4 vertex   : POSITION;
    34.                 float4 color    : COLOR;
    35.                 float2 texcoord : TEXCOORD0;
    36.             };
    37.             struct v2f
    38.             {
    39.                 float4 vertex   : SV_POSITION;
    40.                 fixed4 color    : COLOR;
    41.                 half2 texcoord  : TEXCOORD0;
    42.             };
    43.          
    44.             fixed4 _Color;
    45.             v2f vert(appdata_t IN)
    46.             {
    47.                 v2f OUT;
    48.  
    49.                 OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex);
    50.                 OUT.texcoord = IN.texcoord;  
    51.                
    52.                 OUT.color = EncodeFloatRGBA(IN.color.r);
    53.  
    54.                 #ifdef PIXELSNAP_ON
    55.                 OUT.vertex = UnityPixelSnap (OUT.vertex);
    56.                 #endif
    57.                 return OUT;
    58.             }
    59.             sampler2D _MainTex;
    60.             fixed4 frag(v2f IN) : COLOR
    61.             {
    62.                 half4 texcol = tex2D (_MainTex, IN.texcoord);            
    63.                 texcol.rgb *= IN.color.rgb;
    64.                 return texcol;
    65.             }          
    66.  
    67.  
    68.         ENDCG
    69.         }
    70.     }
    71.     Fallback "Sprites/Default"
    72. }
    For now im just using the .r component which should be the color.

    But for some reason the result is just black and I cant figure out why:

    upload_2014-11-13_15-52-16.png

    Does anyone have a clue where im going wrong?
     
  2. tonemcbride

    tonemcbride

    Joined:
    Sep 7, 2010
    Posts:
    1,077
    I might be totally wrong but it could be that the SpriteRenderer mesh just doesn't have any colour data in it's mesh. Perhaps it's colour was just controlled by a shader param like this:

    Properties {
    _Color ("Main Color", Color) = (0.5,0.5,0.5,1)
    _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
    }

    When you set spriterenderer.color it might just be setting the material colour.

    You could add that to your shader and just use _Color instead of the vertex colour. Not sure if that would work though as I think you wanted to be able to have them in the vertex colour for a reason?
     
  3. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,414
    It is probably related to the decoding and encoding part. I would port EncodeFloatRGBA to C# and printf what its output is. It's probably not even close to the input color. It's the only thing that could go wrong in this shader :)

    BTW, EncodeFloatRGBA has issues with encoding a value of 1 (it's a comment above the function at least).
     
  4. Mikeysee

    Mikeysee

    Joined:
    Oct 14, 2013
    Posts:
    155
    Im pretty sure thats not the case as you can tint each sprite individually and they are still batched, the only way (as far as I am aware) that this is possible is via vertex data.
     
  5. Mikeysee

    Mikeysee

    Joined:
    Oct 14, 2013
    Posts:
    155
    Good idea, ill give this a go and let you know!
     
  6. Marco-Sperling

    Marco-Sperling

    Joined:
    Mar 5, 2012
    Posts:
    620
    Any success?
    I have considered going down the same track for the "per button effects" we want to have in our next title. But if there's no actual vertex color data sent to the UI buttons it won't make much sense.
     
  7. Mikeysee

    Mikeysee

    Joined:
    Oct 14, 2013
    Posts:
    155
    I haven't gotten round to it yet unfortunately. For now im packing it in the alpha channel using a simple divide:

    Code (CSharp):
    1.  public Color GetColor(Color color, float brightness, float greyscale, float somethingelse)
    2.         {
    3.             return new Color(color.r, color.g, color.b, brightness / 2f);
    4.         }