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

Recolor Shader + Single Draw Call

Discussion in 'Shaders' started by Zelek, Aug 26, 2013.

  1. Zelek

    Zelek

    Joined:
    Jun 12, 2010
    Posts:
    87
    I'm not familiar with writing shaders, so I was hoping someone could tell me if the following is even possible:

    I'd like to have a single texture with red, green, and blue sections, and then have a shader that recolors those specific areas of the texture. The catch is that I'd like to have hundreds of game object instances that all use this single texture, but I want each one to be colored differently, without incurring an extra draw call for each instance.

    For example, consider this texture:
    $test_small.png

    Let's say I want to create game object A, which recolors the red circle to brown, the green circle to yellow, and the blue circle to orange. Then I want to create game object B, which recolors the circles to different colors. Is it possible to do this without a separate material instance (and hence an additional draw call)?

    I read this post that seemed to suggest this might be possible with a vertex shader, but I may have misunderstood.
     
  2. bricevdm

    bricevdm

    Joined:
    Nov 4, 2009
    Posts:
    34
    yes you can :)

    If some material parameters need to change from one object to another and you don't want to lose batching you need to store them some other way.
    You can use vertex data (hence the confusion with vertex shader i guess). It now all depends on how you use those. An easy example: you have multiple sprites with the same monster texture but they need to have different colors. You store the colors into vertex colors, and most shaders will use this color and multiply it on top of the texture, *voila*. Since vertex colors are *per vertex*, like any other data (position, Uvs, ..) all the sprites will be batched as one draw call.

    Your case is a bit more complex so you need to write a new shader to support 3 colors.
    If you need full RGB control over the colors then you should use multiple vertex color sets (and I'm not sure you can do this, pretty sure it's tricky)
    If you have a limited number of colors however, you could store them into a texture ( something like a horizontal rainbow gradient ), and store a single parameter that represents where the appropriate color is on that texture (instead of 3 parameters for a RGB color).

    something like this (not tested)

    Code (csharp):
    1.  
    2. sampler2D _MainTex; // your texture masks
    3. sampler2D _ColorTex; // the rainbow gradient
    4.  
    5. struct v2f {
    6.     float4 pos   : SV_POSITION; // regular vertex pos, don't touch this
    7.     half2  uv   : TEXCOORD0;     // the UVs for the main texture
    8.     half4  color : TEXCOORD1; // the parameters
    9. };
    10.  
    11. v2f vert (appdata_full v)
    12. {
    13.     v2f o;
    14.     o.pos = mul (UNITY_MATRIX_MVP, v.vertex); // transform position to screen space
    15.    
    16.     o.uv.xy = v.texcoord.xy; // pass Uvs
    17.     o.color = v.color; // pass vertex color
    18.    
    19.     return o;
    20. }
    21.  
    22. half4 frag (v2f i) : COLOR
    23. {
    24.     fixed4 bitmap = tex2D(_MainTex, i.uv.xy); // your packed texture masks, one in each color channel, up to 4 including alpha
    25.    
    26. return fixed4 (
    27.         tex2D(_ColorTex, half2( i.color.x , 0.5f) ).rgb * bitmap.r
    28. +      tex2D(_ColorTex, half2( i.color.y , 0.5f) ).rgb * bitmap.g
    29. +      tex2D(_ColorTex, half2( i.color.z , 0.5f) ).rgb * bitmap.b
    30. ,1.0f);
    31. }
    32.  
     
  3. Zelek

    Zelek

    Joined:
    Jun 12, 2010
    Posts:
    87
    Thank you so much for the great explanation and the clear code. I'm going to experiment with this and see how far I can get. You mentioned storing "a single parameter that represents where the appropriate color is on that texture" - is that parameter used in the code above? How would I go about specifying the value of the parameter from the C# side of things?
     
  4. Zelek

    Zelek

    Joined:
    Jun 12, 2010
    Posts:
    87
    So I've got the shader to the point where I can recolor specific areas of the texture, but I'm still not sure how I'd go about setting a parameter in C# that I could use in the shader code. Does anyone have an idea?
     
  5. CatsPawGames

    CatsPawGames

    Joined:
    Jun 4, 2014
    Posts:
    443