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

How to combine color with texture while not affecting whites blacks

Discussion in 'Shaders' started by Essential, Oct 29, 2012.

  1. Essential

    Essential

    Joined:
    Sep 8, 2011
    Posts:
    265
    This is a bit of a beginner question and I think it might be related to one of the combine / add / subtract commands but I'm not sure...

    Basically I'm trying to import a font material that has an embossing effect applied to it (which comprises of blacks and whites around the edges of the text), and then be able to change the color of the text while retaining the emboss effect.

    Right now if I change the color property of the material, the black stays fine but the white outline will change color too, which I don't want. How can I retain the blacks and whites while color-changing the rest of the text?
     
  2. Martin-Kraus

    Martin-Kraus

    Joined:
    Feb 18, 2011
    Posts:
    617
    Which (built-in?) shaders are you using? Can you give some screenshots?
     
  3. Essential

    Essential

    Joined:
    Sep 8, 2011
    Posts:
    265
  4. Martin-Kraus

    Martin-Kraus

    Joined:
    Feb 18, 2011
    Posts:
    617
    Could you also show the original texture? On which platform should it work? (The shader that you are using is a fixed-function shader which should work everywhere, this is probably not possible for a shader which does what you are asking for.)
     
  5. Essential

    Essential

    Joined:
    Sep 8, 2011
    Posts:
    265
    It's very close to what I want. Obviously because the color of the text in the original image is white it's obscuring the 'white' part of the emboss effect. But if I could change the text color to pink or something (default color is unimportant) and then only change that color, it would work.

    Platform is for mobile, so I'd like it to be as efficient as possible.

    Texture I'm using (top), potential texture for a different shader (bottom)



     
  6. Martin-Kraus

    Martin-Kraus

    Joined:
    Feb 18, 2011
    Posts:
    617
    Can you change the colors freely? E.g. red for the font color, green for dark edges, blue for white edges? That would make it easier to write a shader that takes the texture and three user-defined colors (text color, color of dark edges, color of light edges) and then combines the three colors according to the red, green, and blue components of the color in the texture.
     
    Last edited: Oct 29, 2012
  7. Essential

    Essential

    Joined:
    Sep 8, 2011
    Posts:
    265
    Yep, I can make them any color. So long as they can be turned back into black / white that sounds like it could work…
     
  8. Martin-Kraus

    Martin-Kraus

    Joined:
    Feb 18, 2011
    Posts:
    617
    Code (csharp):
    1.  
    2. Shader "Custom/NewShader" {
    3.     Properties {
    4.         _MainTex ("Base (RGBA)", 2D) = "white" {}
    5.         _RedColor ("color for red part", Color) = (1,1,0,1)
    6.         _GreenColor ("color for green part", Color) = (1,1,1,1)
    7.         _BlueColor ("color for blue part", Color) = (0,0,0,1)
    8.     }
    9.     SubShader {
    10.         Tags { "Queue"="Transparent" }
    11.  
    12.         CGPROGRAM
    13.         #pragma surface surf Lambert alpha
    14.  
    15.         sampler2D _MainTex;
    16.         half4 _RedColor;
    17.         half4 _GreenColor;
    18.         half4 _BlueColor;
    19.  
    20.         struct Input {
    21.             float2 uv_MainTex;
    22.         };
    23.  
    24.         void surf (Input IN, inout SurfaceOutput o) {
    25.             half4 c = tex2D (_MainTex, IN.uv_MainTex);
    26.             o.Emission = half3(_RedColor * c.r + _GreenColor * c.g + _BlueColor * c.b);  
    27.             o.Alpha = c.a;
    28.         }
    29.         ENDCG
    30.     }
    31.     FallBack "Diffuse"
    32. }
    33.  
    Hope this helps.
     
  9. Molt

    Molt

    Joined:
    Sep 6, 2011
    Posts:
    103
    The technique you were originally after for the 'white is white, black is black, mid-grey is the tint color' is an overlay blend, or hard light blend. I don't believe Unity supports this as a shader option but it's definitely possible to write a shader which'll do it.

    I'm at work at the moment so can't play with this myself but I may have time to do it tonight- even if I don't though the algorithm (which must be done per-channel) is at http://en.wikipedia.org/wiki/Blend_modes#Overlay
     
    Last edited: Oct 31, 2012
  10. Molt

    Molt

    Joined:
    Sep 6, 2011
    Posts:
    103
    Okay, had time for a quick look into this properly and seem to have something that works nicely- just make sure that the base text is mid-grey with brighter grey/white highlights and darker grey/black shadows and things should be happy. This version's actually based on a nicer implementation of the overlay effect than the Wikipedia link supplied, it uses the luminance of the texture to drive it.

    The shader, heavily based on one of Unity's provided GUI shaders with only a new fragment shader, is:

    Code (csharp):
    1.  
    2. Shader "Custom/OverlayBlend"
    3. {
    4.     Properties {
    5.         _MainTex ("Texture", any) = "" {}
    6.         _Color ("Blend Color", Color) = (0.2, 0.3, 1 ,1)
    7.     }
    8.  
    9.     SubShader {
    10.  
    11.         Tags { "ForceSupported" = "True" "RenderType"="Overlay" }
    12.        
    13.         Lighting Off
    14.         Blend SrcAlpha OneMinusSrcAlpha
    15.         Cull Off
    16.         ZWrite Off
    17.         Fog { Mode Off }
    18.         ZTest Always
    19.        
    20.         Pass { 
    21.             CGPROGRAM
    22.             #pragma vertex vert
    23.             #pragma fragment frag
    24.             #pragma fragmentoption ARB_precision_hint_fastest
    25.  
    26.             #include "UnityCG.cginc"
    27.  
    28.             struct appdata_t {
    29.                 float4 vertex : POSITION;
    30.                 fixed4 color : COLOR;
    31.                 float2 texcoord : TEXCOORD0;
    32.             };
    33.  
    34.             struct v2f {
    35.                 float4 vertex : POSITION;
    36.                 fixed4 color : COLOR;
    37.                 float2 texcoord : TEXCOORD0;
    38.             };
    39.  
    40.             sampler2D _MainTex;
    41.  
    42.             uniform float4 _MainTex_ST;
    43.             uniform float4 _Color;
    44.            
    45.             v2f vert (appdata_t v)
    46.             {
    47.                 v2f o;
    48.                 o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
    49.                 o.color = v.color;
    50.                 o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
    51.                 return o;
    52.             }
    53.  
    54.             fixed4 frag (v2f i) : COLOR
    55.             {
    56.                 // Get the raw texture value
    57.                 float4 texColor = tex2D(_MainTex, i.texcoord);
    58.                 // Calculate the luminance of the texture color
    59.                 float luminance =  dot(texColor, fixed4(0.2126, 0.7152, 0.0722, 0));
    60.                 // Declare the output structure
    61.                 fixed4 output = 0;
    62.                
    63.                 // The actual Overlay/High Light method is based on the shader
    64.                 if (luminance < 0.5) {
    65.                     output = 2.0 * texColor * _Color;
    66.                 } else {
    67.                     // We need a white base for a lot of the algorith,
    68.                     fixed4 white = fixed4(1,1,1,1);
    69.                     output = white - 2.0 * (white - texColor) * (white - _Color);
    70.                 }
    71.                
    72.                 // The alpha can actually just be a simple blend of the two- makes things nicely controllable in both
    73.                 // texture and color
    74.                 output.a  = texColor.a * _Color.a;
    75.                 return output;
    76.             }
    77.             ENDCG
    78.         }
    79.     }  
    80.  
    81.    
    82.     SubShader {
    83.  
    84.         Tags { "ForceSupported" = "True" "RenderType"="Overlay" }
    85.  
    86.         Lighting Off
    87.         Blend SrcAlpha OneMinusSrcAlpha
    88.         Cull Off
    89.         ZWrite Off
    90.         Fog { Mode Off }
    91.         ZTest Always
    92.        
    93.         BindChannels {
    94.             Bind "vertex", vertex
    95.             Bind "color", color
    96.             Bind "TexCoord", texcoord
    97.         }
    98.        
    99.         Pass {
    100.             SetTexture [_MainTex] {
    101.                 combine primary * texture DOUBLE, primary * texture DOUBLE
    102.             }
    103.         }
    104.     }
    105.  
    106.     Fallback off
    107. }
    108.  
    ..also here is the project I've built this in- ( View attachment $Multiply Blend.zip ) , complete with a font image built for it. I've used Unity 4 Beta for this (No longer have Unity 3 on this machine) but if you're running a lower version then you can at least get the assets from it, there's nothing fancy in the setup, it's just a shader with a texture combined in a material and mapped to a plane.
     
  11. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    I was playing around with this shader, and found the following version of the fragment program to be a bit more efficient and easier to read:
    Code (csharp):
    1.             fixed4 frag (v2f i) : COLOR
    2.             {
    3.                 // Get the raw texture value
    4.                 float4 texColor = tex2D(_MainTex, i.texcoord);
    5.                 // Calculate the twiceLuminance of the texture color
    6.                 float twiceLuminance =  dot(texColor, fixed4(0.2126, 0.7152, 0.0722, 0)) * 2;
    7.                 // Declare the output structure
    8.                
    9.                 fixed4 output = 0;
    10.                
    11.                 // The actual Overlay/High Light method is based on the shader
    12.                 if (twiceLuminance < 1) {
    13.                     output = lerp(fixed4(0, 0, 0, 0), _Color, twiceLuminance);
    14.                 } else {
    15.                     output = lerp(_Color, fixed4(1, 1, 1, 1), twiceLuminance - 1);
    16.                 }
    17.                
    18.                 // The alpha can actually just be a simple blend of the two-
    19.                 // makes things nicely controllable in both texture and color
    20.                 output.a  = texColor.a * _Color.a;
    21.                 return output;
    22.             }
     
  12. Molt

    Molt

    Joined:
    Sep 6, 2011
    Posts:
    103
    It does seem to be both more efficient and readable- very nicely done.
     
  13. Essential

    Essential

    Joined:
    Sep 8, 2011
    Posts:
    265
    Wow! I move apartments and come back to find a fully-functioning awesome shader! Thanks heaps Martin, Molt, and Daniel. :D

    One little observation about this shader though… If I place the text using this shader in front of an object that uses the Unlit/Transparent shader, the text disappears in front of it. It appears in front of objects using different shaders, but not that one… Any ideas?
     
  14. Essential

    Essential

    Joined:
    Sep 8, 2011
    Posts:
    265
    So I'm still struggling with the little display problem when the text is positioned above an Unlit/Transparent object… What I tried was going into this shader and changing the ZWrite to On, which displays the text but also gives a weird display artifact where I can see the background image beneath. Here's a few examples:

    With ZWrite Off, text is obscured by layer:




    With ZWrite On, text is visible above layer but weirdly tunnels through underlay into background:

     
  15. Martin-Kraus

    Martin-Kraus

    Joined:
    Feb 18, 2011
    Posts:
    617
    Use Tags { "Queue"="Transparent+1" } to make it appear in front of other transparent objects.
     
  16. Essential

    Essential

    Joined:
    Sep 8, 2011
    Posts:
    265
    PERFECT! Thanks Martin :)


    By the way, just out of curiosity, what do you think was the better text shader to use. Your one or Molt's shader?
     
  17. Essential

    Essential

    Joined:
    Sep 8, 2011
    Posts:
    265
    So this worked great but I've just discovered it introduced a new problem… If I lay another transparent object in front (say a pop-up), the text comes through and sits on top (as you said), but is there some way I can make this text sit above transparent objects by using the Z-axis and not the Transparency queue?

    I don't know much about shader writing; Z-write, transparency queue, etc. so any help would be greatly appreciated. :)
     
  18. Martin-Kraus

    Martin-Kraus

    Joined:
    Feb 18, 2011
    Posts:
    617
    You can also use Tags { "Queue"="Transparent+2" } or Tags { "Queue"="Transparent+3" } etc. for shaders that are even further in front.

    An alternative probably is to use multiple cameras with different depths.
     
  19. Essential

    Essential

    Joined:
    Sep 8, 2011
    Posts:
    265
    Yeah, I don't think extending the transparency queue for the other objects is the right solution... Otherwise I'd end up with several shader duplicates simply to layer the objects in front of each other. The multiple camera thing could also be a problem.

    Can you explain what it is about this shader that makes the text behave so that it 'tunnels' through the background object when I have Z-write on? That seems to be the problem I need to solve...
     
  20. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    There is no need to use custom queues for this. Just use the normal transparent settings (for an example, look at the Particles/Alpha Blended source). Take a look at the manual for explanations of these things, and read about Z buffering if you don't understand it.

    Use these tags:

    Code (csharp):
    1. Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
    Use the standard Transparent queue. This will allow your shader to layer properly with other transparent objects:

    Code (csharp):
    1. ZWrite Off
    Get rid of ZTest Always. ZTest Always means always display, regardless of the value in the depth buffer. The default, if you want to make it explicit, is:

    Code (csharp):
    1. ZTest LEqual
     
    Last edited: Feb 1, 2013
  21. Essential

    Essential

    Joined:
    Sep 8, 2011
    Posts:
    265
    Ah… That solved it.

    Thanks for the help Daniel :)
     
  22. dasbin

    dasbin

    Joined:
    Jan 14, 2012
    Posts:
    261
    Hi guys,

    Very interesting thread. I'm trying to modify this shader to my needs such that an output color above middle grey becomes an additive blend. It this possible without resorting to a GrabPass? I suspect the built-in blend modes make this impossible, but it's worth checking.
     
  23. Gooseman_1977

    Gooseman_1977

    Joined:
    Aug 15, 2013
    Posts:
    89
    Is this shader as perfomance friendly as the "additive" or "multiply" shaders? (ie. does it result in overdraw issues when the shader is applied to large sprites that are overlapping ,like smoke)
    I'm thinking of using this on my mobile device and I want to avoid any alpha blending shaders as they result in massive overdraw with my smoke sprites.
     
  24. ashwinFEC

    ashwinFEC

    Joined:
    May 19, 2013
    Posts:
    48
    I have found a post that shows an example on how to eliminate branching http://xissburg.com/eliminating-branches-in-shaders/

    This might improve performance if you target mobile GPUs

    Code (CSharp):
    1.  
    2. fixed4 frag (v2f i) : COLOR           {
    3.     // Get the raw texture value
    4.     float4 texColor = tex2D(_MainTex, i.texcoord);
    5.  
    6.     fixed3 br = clamp(sign(texColor.rgb - fixed3(0.5,0.5,0.5)), fixed3(0.0,0.0,0.0), fixed3(1.0,1.0,1.0));
    7.     fixed3 multiply = 2.0 * texColor.rgb *  _Color.rgb;
    8.     fixed3 screen = fixed3(1.0,1.0,1.0) - 2.0 * (fixed3(1.0,1.0,1.0) - texColor.rgb)*(fixed3(1.0,1.0,1.0) - _Color.rgb);
    9.     // If br is 0.0, overlay will be multiply (which translates to if (base < 0.5) { return multiply; }).
    10.     // if bt is 1.0, overlay will be screen (which translates to if (base >= 0.5) { return screen; }).
    11.     fixed4 output = fixed4(lerp(multiply, screen, br),1);
    12.     output.a = texColor.a * _Color.a;
    13.     return output;
    14. }
    15.  
     
    Andrey-Postelzhuk likes this.