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

Problem with getting hue shift shader right.

Discussion in 'Shaders' started by beeky, May 12, 2011.

  1. beeky

    beeky

    Joined:
    Apr 6, 2009
    Posts:
    25
    For one of my projects I need to hue shift some elements.

    I figured out that this shader could help me. I manage to hue shift between colors but I loose my transparency in my sprite atlas.

    anybody an idea how to turn this on?

    tx

    Code (csharp):
    1.  
    2. Shader "HSB_HSV_Colorpicker" {
    3.   Properties {
    4.       _MainTex ("Texture", 2D) = "white" {}
    5.       _HueShift("HueShift", Float) = 0
    6.     }
    7.     SubShader {
    8.       Tags { "RenderType" = "Opaque" }
    9.       CGPROGRAM
    10.       #pragma surface surf Lambert
    11.       #pragma target 3.0
    12.          
    13.         float3 rgb_to_hsv_no_clip(float3 RGB)
    14.         {
    15.                 float3 HSV;
    16.            
    17.          float minChannel, maxChannel;
    18.          if (RGB.x > RGB.y) {
    19.           maxChannel = RGB.x;
    20.           minChannel = RGB.y;
    21.          }
    22.          else {
    23.           maxChannel = RGB.y;
    24.           minChannel = RGB.x;
    25.          }
    26.          
    27.          if (RGB.z > maxChannel) maxChannel = RGB.z;
    28.          if (RGB.z < minChannel) minChannel = RGB.z;
    29.            
    30.                 HSV.xy = 0;
    31.                 HSV.z = maxChannel;
    32.                 float delta = maxChannel - minChannel;             //Delta RGB value
    33.                 if (delta != 0) {                    // If gray, leave H  S at zero
    34.                    HSV.y = delta / HSV.z;
    35.                    float3 delRGB;
    36.                    delRGB = (HSV.zzz - RGB + 3*delta) / (6.0*delta);
    37.                    if      ( RGB.x == HSV.z ) HSV.x = delRGB.z - delRGB.y;
    38.                    else if ( RGB.y == HSV.z ) HSV.x = ( 1.0/3.0) + delRGB.x - delRGB.z;
    39.                    else if ( RGB.z == HSV.z ) HSV.x = ( 2.0/3.0) + delRGB.y - delRGB.x;
    40.                 }
    41.                 return (HSV);
    42.         }
    43.  
    44.         float3 hsv_to_rgb(float3 HSV)
    45.         {
    46.                 float3 RGB = HSV.z;
    47.            
    48.                    float var_h = HSV.x * 6;
    49.                    float var_i = floor(var_h);   // Or ... var_i = floor( var_h )
    50.                    float var_1 = HSV.z * (1.0 - HSV.y);
    51.                    float var_2 = HSV.z * (1.0 - HSV.y * (var_h-var_i));
    52.                    float var_3 = HSV.z * (1.0 - HSV.y * (1-(var_h-var_i)));
    53.                    if      (var_i == 0) { RGB = float3(HSV.z, var_3, var_1); }
    54.                    else if (var_i == 1) { RGB = float3(var_2, HSV.z, var_1); }
    55.                    else if (var_i == 2) { RGB = float3(var_1, HSV.z, var_3); }
    56.                    else if (var_i == 3) { RGB = float3(var_1, var_2, HSV.z); }
    57.                    else if (var_i == 4) { RGB = float3(var_3, var_1, HSV.z); }
    58.                    else                 { RGB = float3(HSV.z, var_1, var_2); }
    59.            
    60.            return (RGB);
    61.         }
    62.  
    63.       struct Input {
    64.           float2 uv_MainTex;
    65.       };
    66.      
    67.       sampler2D _MainTex;
    68.       float _HueShift;
    69.      
    70.       void surf (Input IN, inout SurfaceOutput o) {
    71.           o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
    72.          
    73.           float3 hsv = rgb_to_hsv_no_clip(o.Albedo.xyz);
    74.           hsv.x+=_HueShift;
    75.                  
    76.           if ( hsv.x > 1.0 ) { hsv.x -= 1.0; }
    77.           o.Albedo = half3(hsv_to_rgb(hsv));
    78.       }
    79.      
    80.       ENDCG
    81.     }
    82.     Fallback "Diffuse"
    83.   }
    84.  
     
  2. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    Below the line;
    Code (csharp):
    1.  
    2. o.Albedo = half3(hsv_to_rgb(hsv));
    3.  
    add
    Code (csharp):
    1.  
    2. o.Alpha = tex2D (_MainTex, IN.uv_MainTex).a;
    3.  
     
  3. beeky

    beeky

    Joined:
    Apr 6, 2009
    Posts:
    25
    I notice no difference when adding that extra line.
     
  4. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    Oh, you might need to change
    Tags { "RenderType" = "Opaque" }
    to
    Tags { "RenderType" = "Transparent" }
     
  5. equil

    equil

    Joined:
    Jan 18, 2010
    Posts:
    15
    you want to change the surface shader line to have alpha.
    Code (csharp):
    1. #pragma surface surf Lambert alpha
    also change the rendertype to "Transparent".
     
  6. beeky

    beeky

    Joined:
    Apr 6, 2009
    Posts:
    25
    Great it works. Thank you all.

    below the hue shift shader where hue and saturation can be changed.

    Code (csharp):
    1.  
    2. Shader "HSB_HSV_Colorpicker" {
    3.   Properties {
    4.       _MainTex ("Texture", 2D) = "white" {}
    5.       _HueShift("HueShift", Float) = 0
    6.       _SaturationShift("SaturationShift", Float) = 1.0
    7.     }
    8.     SubShader {
    9.       Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType" = "Transparent" }
    10.       ZWrite Off
    11.       Blend SrcAlpha OneMinusSrcAlpha
    12.       Cull Off
    13.       CGPROGRAM
    14.       //#pragma surface surf Lambert
    15.       #pragma surface surf Lambert alpha
    16.       #pragma target 3.0
    17.          
    18.         float3 rgb_to_hsv_no_clip(float3 RGB)
    19.         {
    20.                 float3 HSV;
    21.            
    22.          float minChannel, maxChannel;
    23.          if (RGB.x > RGB.y) {
    24.           maxChannel = RGB.x;
    25.           minChannel = RGB.y;
    26.          }
    27.          else {
    28.           maxChannel = RGB.y;
    29.           minChannel = RGB.x;
    30.          }
    31.          
    32.          if (RGB.z > maxChannel) maxChannel = RGB.z;
    33.          if (RGB.z < minChannel) minChannel = RGB.z;
    34.            
    35.                 HSV.xy = 0;
    36.                 HSV.z = maxChannel;
    37.                 float delta = maxChannel - minChannel;             //Delta RGB value
    38.                 if (delta != 0) {                    // If gray, leave H  S at zero
    39.                    HSV.y = delta / HSV.z;
    40.                    float3 delRGB;
    41.                    delRGB = (HSV.zzz - RGB + 3*delta) / (6.0*delta);
    42.                    if      ( RGB.x == HSV.z ) HSV.x = delRGB.z - delRGB.y;
    43.                    else if ( RGB.y == HSV.z ) HSV.x = ( 1.0/3.0) + delRGB.x - delRGB.z;
    44.                    else if ( RGB.z == HSV.z ) HSV.x = ( 2.0/3.0) + delRGB.y - delRGB.x;
    45.                 }
    46.                 return (HSV);
    47.         }
    48.  
    49.         float3 hsv_to_rgb(float3 HSV)
    50.         {
    51.                 float3 RGB = HSV.z;
    52.            
    53.                    float var_h = HSV.x * 6;
    54.                    float var_i = floor(var_h);   // Or ... var_i = floor( var_h )
    55.                    float var_1 = HSV.z * (1.0 - HSV.y);
    56.                    float var_2 = HSV.z * (1.0 - HSV.y * (var_h-var_i));
    57.                    float var_3 = HSV.z * (1.0 - HSV.y * (1-(var_h-var_i)));
    58.                    if      (var_i == 0) { RGB = float3(HSV.z, var_3, var_1); }
    59.                    else if (var_i == 1) { RGB = float3(var_2, HSV.z, var_1); }
    60.                    else if (var_i == 2) { RGB = float3(var_1, HSV.z, var_3); }
    61.                    else if (var_i == 3) { RGB = float3(var_1, var_2, HSV.z); }
    62.                    else if (var_i == 4) { RGB = float3(var_3, var_1, HSV.z); }
    63.                    else                 { RGB = float3(HSV.z, var_1, var_2); }
    64.            
    65.            return (RGB);
    66.         }
    67.  
    68.       struct Input {
    69.           float2 uv_MainTex;
    70.       };
    71.      
    72.       sampler2D _MainTex;
    73.       float _HueShift;
    74.       float _SaturationShift;
    75.      
    76.       void surf (Input IN, inout SurfaceOutput o) {
    77.           o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
    78.          
    79.           float3 hsv = rgb_to_hsv_no_clip(o.Albedo.xyz);
    80.           hsv.x+=_HueShift;
    81.           hsv.y = _SaturationShift;
    82.          
    83.           if ( hsv.x > 1.0 ) { hsv.x -= 1.0; }
    84.           o.Albedo = half3(hsv_to_rgb(hsv));
    85.           o.Alpha = tex2D (_MainTex, IN.uv_MainTex).a;
    86.       }
    87.      
    88.       ENDCG
    89.     }
    90.     Fallback "Diffuse"
    91.   }
    92.  
     
  7. gametr4x

    gametr4x

    Joined:
    Apr 22, 2009
    Posts:
    86
    I've just tried using the shader, but I'm still getting some weird glitches with certain pixels. I have white or almost white pixels which are displayed as red, and a rim of pixels around them which are rendered with various strange colours. This happens even when the HueShift variable is set to 0, so I'm guessing there is some kind of round-off error occurring.

    Here's an example of the problem:



    Any idea where the problem might be? Perhaps I can do something with the image to prevent this?

    EDIT: Perhaps it has something to do with the platform I'm running it on, since my major version of OpenGL reports 2, but this is compiled for 3.0

    EDIT2: This seems to be a likely source of the issue. I ported the code to a direct resource edit (using Texture2D's SetPixels for OpenGL ES 1.1 compatibility) and the red eye-syndrome doesn't happen. There still are some strange glitches with the borders of the eye, however.

    EDIT3: My bad, I ported it wrong, which caused a beneficial bug but the hue shifting didn't occur properly anymore. Both issues still occur, but I can minimize the border glitches by checking "delta > 0.05" (or something similar) instead of exactly checking "delta != 0"
     
    Last edited: Jun 1, 2011
  8. gametr4x

    gametr4x

    Joined:
    Apr 22, 2009
    Posts:
    86
    The problem appears to be the saturation shift.

    If you input a grayscale color into the functions, then changing the HSV.y variable to 1 (for full saturation), will result in the G B channels of the output color to become 0. That's obviously because var_i is 0, and in that case, var_1 = HVS.z * ( 1 - 1 ) = 0, the same happens with var_3.

    This is why the white eye became red. Since R = HSV.z (original color), G B = 0.

    So... the saturation feature is nice, but I don't use it so I've removed it, and the shader works perfectly now. However, I'd recommend looking into fixing the problem as well :p

    EDIT: Easy fix. multiply by the saturation instead of straight setting it.
     
    Last edited: Jun 1, 2011
  9. gametr4x

    gametr4x

    Joined:
    Apr 22, 2009
    Posts:
    86
    Sorry to hi-jack the post, but I'm trying to get this shader to work with OpenGL ES 2.0

    I've already refactored the shader to fit within the 64 arithmetic instruction limit (it was previously using a lot of useless if else statements to save on performance I guess, which prevented it from compiling at all), and converted it to a vertex/fragment shader, as opposed to purely a surface shader. It's a bit messy atm, since I've never made any shaders like this before in Unity.

    Here's the code I have right now, which compiles and runs fine in emulated ES 2.0, but on the device the color doesn't change (which makes me think it's using the fall-back)

    Any ideas?

    The only thing I can think of is possibly the divide by 0 which may occur when delta == 0 (but this doesn't seem to cause any issues when I test it in unity), and I tried adding a small value (0.01f) to the delta at all times so it wouldn't divide by zero. No luck there...

    Code (csharp):
    1.  
    2. Shader "HSB_HSV_Colorpicker" {
    3.   Properties {
    4.       _TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5)
    5.       _MainTex ("Texture", 2D) = "white" {}
    6.       _HueShift("HueShift", Range(0,1) ) = 0
    7.       _Sat("Saturation", Range(0, 1)) = 0
    8.     }
    9.     SubShader {
    10.    
    11.        
    12.       Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType" = "Transparent" }
    13.       ZWrite Off
    14.       Blend SrcAlpha OneMinusSrcAlpha
    15.       Cull Off
    16.      
    17.     Pass
    18.     {
    19.       CGPROGRAM
    20.       #pragma vertex vert
    21.       #pragma fragment surf Lambert alpha
    22.       #pragma target 2.0
    23.      
    24.       #include "UnityCG.cginc"
    25.          
    26.     struct v2f {
    27.         float4  pos : SV_POSITION;
    28.         float2  uv : TEXCOORD0;
    29.     };
    30.  
    31.     float4 _MainTex_ST;
    32.          
    33.     v2f vert (appdata_base v)
    34.     {
    35.         v2f o;
    36.         o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    37.         o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);
    38.         return o;
    39.     }
    40.      
    41.     float3 rgb_to_hsv_no_clip(float3 RGB)
    42.     {
    43.         float3 HSV = 0;
    44.        
    45.         float minChannel, maxChannel;
    46.        
    47.         maxChannel = max(RGB.x, RGB.y);
    48.         minChannel = min(RGB.x, RGB.y);
    49.        
    50.         maxChannel = max(RGB.z, maxChannel);
    51.         minChannel = min(RGB.z, minChannel);
    52.        
    53.         HSV.z = maxChannel;
    54.        
    55.         float delta = maxChannel - minChannel;             //Delta RGB value
    56.        
    57.         //if ( delta > 0 )
    58.         //{                    // If gray, leave H  S at zero
    59.             HSV.y = delta / HSV.z;
    60.             float3 delRGB = (HSV.zzz - RGB + 3*delta) / (6*delta);
    61.             if ( RGB.x == HSV.z ) HSV.x = delRGB.z - delRGB.y;
    62.             if ( RGB.y == HSV.z ) HSV.x = ( 1.0 / 3.0 ) + delRGB.x - delRGB.z;
    63.             if ( RGB.z == HSV.z ) HSV.x = ( 2.0 / 3.0 ) + delRGB.y - delRGB.x;
    64.         //}
    65.        
    66.         return (HSV);
    67.     }
    68.    
    69.     float3 hsv_to_rgb(float3 HSV)
    70.     {
    71.         float var_h = HSV.x * 6;
    72.         //float var_i = floor(var_h);   // Or ... var_i = floor( var_h )
    73.         float var_1 = HSV.z * ( 1.0 - HSV.y );
    74.         float var_2 = HSV.z * ( 1.0 - HSV.y * (var_h-floor( var_h )));
    75.         float var_3 = HSV.z * ( 1.0 - HSV.y * (1-(var_h-floor( var_h ))));
    76.        
    77.         float3 RGB = float3(HSV.z, var_1, var_2);
    78.        
    79.         if (var_h < 5)  { RGB = float3(var_3, var_1, HSV.z); }
    80.         if (var_h < 4)  { RGB = float3(var_1, var_2, HSV.z); }
    81.         if (var_h < 3)  { RGB = float3(var_1, HSV.z, var_3); }
    82.         if (var_h < 2)  { RGB = float3(var_2, HSV.z, var_1); }
    83.         if (var_h < 1)  { RGB = float3(HSV.z, var_3, var_1); }
    84.        
    85.         return (RGB);
    86.     }
    87.  
    88.     struct Input
    89.     {
    90.       float2 uv_MainTex;
    91.     };
    92.    
    93.     sampler2D _MainTex;
    94.     float _HueShift;
    95.     float _Sat;
    96.    
    97.     half4 surf (Input IN) : COLOR
    98.     {
    99.       half4 col = tex2D (_MainTex, IN.uv_MainTex);
    100.      
    101.       float3 hsv = rgb_to_hsv_no_clip(col.xyz);
    102.       hsv.x += _HueShift;
    103.       //hsv.y *= _Sat;
    104.      
    105.       if ( hsv.x > 1.0 ) { hsv.x -= 1.0; }
    106.       return half4( half3(hsv_to_rgb(hsv) ), col.a);
    107.     }
    108.      
    109.       ENDCG
    110.     }
    111.     }
    112.     Fallback "Particles/Alpha Blended"
    113.   }
    114.  
    Just ran into this:
    Any idea what that means?
     
    Last edited: Jun 1, 2011
  10. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    This looks like it's getting a bit confused - it's sort of half surface shader, half standard shader.

    You've got multiple input structures and all sorts going on. It's a wonder it runs at all :p

    I've ripped out all the surface shader stuff in there and left you with the same code but in standard vertex/fragment format. Seems to run ok here.

    Code (csharp):
    1.  
    2. Shader "HSB_HSV_Colorpicker" {
    3.     Properties {
    4.         _TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5)
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.         _HueShift("HueShift", Range(0,1) ) = 0
    7.         _Sat("Saturation", Range(0, 1)) = 0
    8.     }
    9.     SubShader {
    10.  
    11.         Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType" = "Transparent" }
    12.         ZWrite Off
    13.         Blend SrcAlpha OneMinusSrcAlpha
    14.         Cull Off
    15.  
    16.         Pass
    17.         {
    18.             CGPROGRAM
    19.             #pragma vertex vert
    20.             #pragma fragment frag
    21.             #pragma target 2.0
    22.  
    23.             #include "UnityCG.cginc"
    24.  
    25.             float3 rgb_to_hsv_no_clip(float3 RGB)
    26.             {
    27.                 float3 HSV = 0;
    28.  
    29.                 float minChannel, maxChannel;
    30.  
    31.                 maxChannel = max(RGB.x, RGB.y);
    32.                 minChannel = min(RGB.x, RGB.y);
    33.  
    34.                 maxChannel = max(RGB.z, maxChannel);
    35.                 minChannel = min(RGB.z, minChannel);
    36.  
    37.                 HSV.z = maxChannel;
    38.  
    39.                 float delta = maxChannel - minChannel;             //Delta RGB value
    40.  
    41.                 //if ( delta > 0 )
    42.                 //{                    // If gray, leave H  S at zero
    43.                     HSV.y = delta / HSV.z;
    44.                     float3 delRGB = (HSV.zzz - RGB + 3*delta) / (6*delta);
    45.                     if ( RGB.x == HSV.z ) HSV.x = delRGB.z - delRGB.y;
    46.                     if ( RGB.y == HSV.z ) HSV.x = ( 1.0 / 3.0 ) + delRGB.x - delRGB.z;
    47.                     if ( RGB.z == HSV.z ) HSV.x = ( 2.0 / 3.0 ) + delRGB.y - delRGB.x;
    48.                 //}
    49.  
    50.                 return (HSV);
    51.             }
    52.  
    53.             float3 hsv_to_rgb(float3 HSV)
    54.             {
    55.                 float var_h = HSV.x * 6;
    56.                 //float var_i = floor(var_h);   // Or ... var_i = floor( var_h )
    57.                 float var_1 = HSV.z * ( 1.0 - HSV.y );
    58.                 float var_2 = HSV.z * ( 1.0 - HSV.y * (var_h-floor( var_h )));
    59.                 float var_3 = HSV.z * ( 1.0 - HSV.y * (1-(var_h-floor( var_h ))));
    60.  
    61.                 float3 RGB = float3(HSV.z, var_1, var_2);
    62.  
    63.                 if (var_h < 5)  { RGB = float3(var_3, var_1, HSV.z); }
    64.                 if (var_h < 4)  { RGB = float3(var_1, var_2, HSV.z); }
    65.                 if (var_h < 3)  { RGB = float3(var_1, HSV.z, var_3); }
    66.                 if (var_h < 2)  { RGB = float3(var_2, HSV.z, var_1); }
    67.                 if (var_h < 1)  { RGB = float3(HSV.z, var_3, var_1); }
    68.  
    69.                 return (RGB);
    70.             }
    71.  
    72.             struct v2f {
    73.                 float4  pos : SV_POSITION;
    74.                 float2  uv : TEXCOORD0;
    75.             };
    76.  
    77.             float4 _MainTex_ST;
    78.  
    79.             v2f vert (appdata_base v)
    80.             {
    81.                 v2f o;
    82.                 o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    83.                 o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    84.                 return o;
    85.             }
    86.  
    87.             sampler2D _MainTex;
    88.             float _HueShift;
    89.             float _Sat;
    90.  
    91.             half4 frag(v2f i) : COLOR
    92.             {
    93.                 half4 col = tex2D(_MainTex, i.uv);
    94.  
    95.                 float3 hsv = rgb_to_hsv_no_clip(col.xyz);
    96.                 hsv.x += _HueShift;
    97.                 //hsv.y *= _Sat;
    98.  
    99.                 if ( hsv.x > 1.0 ) { hsv.x -= 1.0; }
    100.                 return half4( half3(hsv_to_rgb(hsv) ), col.a);
    101.             }
    102.  
    103.             ENDCG
    104.         }
    105.     }
    106.     Fallback "Particles/Alpha Blended"
    107. }
    108.  
     
    Last edited: Jun 1, 2011
    twobob likes this.
  11. gametr4x

    gametr4x

    Joined:
    Apr 22, 2009
    Posts:
    86
    Found the issue already. I forget to add the " : TEXCOORD" macro after the fragment shader's input struct.

    Here's the complete, working code for an OpenGL ES 2.0 (3Gs, 4G, iPad, iPod 4g) compatible hue shift shader:

    Code (csharp):
    1.  
    2.  
    3. Shader "HSB_HSV_Colorpicker" {
    4.   Properties {
    5.       _TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5)
    6.       _MainTex ("Texture", 2D) = "white" {}
    7.       _HueShift("HueShift", Range(0,1) ) = 0
    8.       _Sat("Saturation", Range(0, 1)) = 0
    9.     }
    10.     SubShader {
    11.    
    12.        
    13.       Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType" = "Transparent" }
    14.       ZWrite Off
    15.       Blend SrcAlpha OneMinusSrcAlpha
    16.       Cull Off
    17.      
    18.     Pass
    19.     {
    20.       CGPROGRAM
    21.       #pragma vertex vert
    22.       #pragma fragment surf Lambert alpha
    23.       #pragma target 2.0
    24.      
    25.       #include "UnityCG.cginc"
    26.          
    27.     struct v2f {
    28.         float4  pos : SV_POSITION;
    29.         float2  uv : TEXCOORD;
    30.     };
    31.  
    32.     float4 _MainTex_ST;
    33.          
    34.     v2f vert (appdata_base v)
    35.     {
    36.         v2f o = (v2f)0;
    37.         o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    38.         o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);
    39.         return o;
    40.     }
    41.      
    42.     float3 rgb_to_hsv_no_clip(float3 RGB)
    43.     {
    44.         float3 HSV = 0;
    45.        
    46.         float minChannel, maxChannel;
    47.        
    48.         maxChannel = max(RGB.x, RGB.y);
    49.         minChannel = min(RGB.x, RGB.y);
    50.        
    51.         maxChannel = max(RGB.z, maxChannel);
    52.         minChannel = min(RGB.z, minChannel);
    53.        
    54.         HSV.z = maxChannel;
    55.        
    56.         float delta = maxChannel - minChannel;    //Delta RGB value
    57.        
    58.         HSV.y = delta / HSV.z;
    59.         float3 delRGB = (HSV.zzz - RGB + 3*delta) / (6*delta);
    60.         if ( RGB.x == HSV.z ) HSV.x = delRGB.z - delRGB.y;
    61.         if ( RGB.y == HSV.z ) HSV.x = ( 1.0 / 3.0 ) + delRGB.x - delRGB.z;
    62.         if ( RGB.z == HSV.z ) HSV.x = ( 2.0 / 3.0 ) + delRGB.y - delRGB.x;
    63.        
    64.         return (HSV);
    65.     }
    66.    
    67.     float3 hsv_to_rgb(float3 HSV)
    68.     {
    69.         float var_h = HSV.x * 6;
    70.        
    71.         float var_1 = HSV.z * ( 1.0 - HSV.y );
    72.         float var_2 = HSV.z * ( 1.0 - HSV.y * (var_h-floor( var_h )));
    73.         float var_3 = HSV.z * ( 1.0 - HSV.y * (1-(var_h-floor( var_h ))));
    74.        
    75.         float3 RGB = float3(HSV.z, var_1, var_2);
    76.        
    77.         if (var_h < 5)  { RGB = float3(var_3, var_1, HSV.z); }
    78.         if (var_h < 4)  { RGB = float3(var_1, var_2, HSV.z); }
    79.         if (var_h < 3)  { RGB = float3(var_1, HSV.z, var_3); }
    80.         if (var_h < 2)  { RGB = float3(var_2, HSV.z, var_1); }
    81.         if (var_h < 1)  { RGB = float3(HSV.z, var_3, var_1); }
    82.        
    83.         return (RGB);
    84.     }
    85.  
    86.     struct Input
    87.     {
    88.       float2 uv_MainTex : TEXCOORD;
    89.     };
    90.    
    91.     sampler2D _MainTex;
    92.     float _HueShift;
    93.     float _Sat;
    94.    
    95.     half4 surf (Input IN) : COLOR
    96.     {
    97.       half4 col = tex2D (_MainTex, IN.uv_MainTex);
    98.      
    99.       float3 hsv = rgb_to_hsv_no_clip(col.xyz);
    100.       hsv.x += _HueShift;
    101.       //hsv.y *= _Sat;
    102.      
    103.       if ( hsv.x > 1.0 ) { hsv.x -= 1.0; }
    104.       return half4( half3(hsv_to_rgb(hsv) ), col.a);
    105.     }
    106.      
    107.       ENDCG
    108.     }
    109.     }
    110.     Fallback "Particles/Alpha Blended"
    111.   }
    112.  
    113.  
    Currently the saturation is still commented, since it wouldn't compile otherwise (exceeds 64 arithmetic operations)
     
  12. DtodaB

    DtodaB

    Joined:
    Nov 7, 2011
    Posts:
    1
    I was in need of a hsv shader as well and found the sources above very helpful. However, I found that the solutions above still contain some glitches and so I went on and implemented this matrix based solution here: http://beesbuzz.biz/code/hsv_color_transforms.php

    So here is the shader then: (please note that the hue shift expects input in the range of [0,360] and value and saturation parameters are acting as multipliers)

    Code (csharp):
    1. Shader "Custom/HSVShader" {
    2.     Properties {
    3.         _MainTex ("Texture", 2D) = "white" {}
    4.         _HueShift("HueShift", Float ) = 0
    5.         _Sat("Saturation", Float) = 1
    6.         _Val("Value", Float) = 1
    7.     }
    8.     SubShader {
    9.  
    10.         Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType" = "Transparent" }
    11.         ZWrite Off
    12.         Blend SrcAlpha OneMinusSrcAlpha
    13.         Cull Off
    14.  
    15.         Pass
    16.         {
    17.             CGPROGRAM
    18.             #pragma vertex vert
    19.             #pragma fragment frag
    20.             #pragma target 2.0
    21.  
    22.             #include "UnityCG.cginc"
    23.  
    24.             float3 shift_col(float3 RGB, float3 shift)
    25.             {
    26.             float3 RESULT = float3(RGB);
    27.             float VSU = shift.z*shift.y*cos(shift.x*3.14159265/180);
    28.                 float VSW = shift.z*shift.y*sin(shift.x*3.14159265/180);
    29.                
    30.                 RESULT.x = (.299*shift.z+.701*VSU+.168*VSW)*RGB.x
    31.                         + (.587*shift.z-.587*VSU+.330*VSW)*RGB.y
    32.                         + (.114*shift.z-.114*VSU-.497*VSW)*RGB.z;
    33.                
    34.                 RESULT.y = (.299*shift.z-.299*VSU-.328*VSW)*RGB.x
    35.                         + (.587*shift.z+.413*VSU+.035*VSW)*RGB.y
    36.                         + (.114*shift.z-.114*VSU+.292*VSW)*RGB.z;
    37.                
    38.                 RESULT.z = (.299*shift.z-.3*VSU+1.25*VSW)*RGB.x
    39.                         + (.587*shift.z-.588*VSU-1.05*VSW)*RGB.y
    40.                         + (.114*shift.z+.886*VSU-.203*VSW)*RGB.z;
    41.                
    42.             return (RESULT);
    43.             }
    44.  
    45.             struct v2f {
    46.                 float4  pos : SV_POSITION;
    47.                 float2  uv : TEXCOORD0;
    48.             };
    49.  
    50.             float4 _MainTex_ST;
    51.  
    52.             v2f vert (appdata_base v)
    53.             {
    54.                 v2f o;
    55.                 o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    56.                 o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    57.                 return o;
    58.             }
    59.  
    60.             sampler2D _MainTex;
    61.             float _HueShift;
    62.             float _Sat;
    63.             float _Val;
    64.  
    65.             half4 frag(v2f i) : COLOR
    66.             {
    67.                 half4 col = tex2D(_MainTex, i.uv);
    68.                 float3 shift = float3(_HueShift, _Sat, _Val);
    69.                
    70.                 return half4( half3(shift_col(col, shift)), col.a);
    71.             }
    72.             ENDCG
    73.         }
    74.     }
    75.     Fallback "Particles/Alpha Blended"
    76. }
     
    twobob likes this.
  13. mojtaba64

    mojtaba64

    Joined:
    Aug 3, 2010
    Posts:
    56
    @DtodaB: I need hue shader too and found your shader the best one!

    I don't have access to mac and iphone now and can't test this shader.
    Will it work correctly on 3gs and above? Can anyone test it please?

    Thanks
     
    Last edited: Nov 10, 2011
  14. kkk

    kkk

    Joined:
    Jul 23, 2013
    Posts:
    1
    I needed a similar shader for palette shifting some parts of a vehicle and not others (things like wheels and lights that need to stay the same for all vehicles). I wound up adding a bit to DtodaB's code, and thought others might find it useful for character customization and so on, so here you go:


    Code (CSharp):
    1. Shader "Custom/LayeredHSVShift"
    2. {
    3. Properties {
    4.     _Color ("Color", Color) = (1,1,1,1)
    5.     _MainTex ("Color to Shift (RGB) Trans (A)", 2D) = "white" {}
    6.  
    7.     _HueShift ("Hue (0-360)", Float) = 0
    8.     _SatShift ("Saturation", Float) = 1
    9.     _ValShift ("Value (brightness)", Float) = 1
    10.  
    11.     _Tex2 ("Unshifted Color (RGB) Trans (A)", 2D) = "white" {}
    12.  
    13.     _Cutoff ("Alpha cutoff", Range(0,1)) = 0.5
    14.  
    15. }
    16.  
    17. SubShader {
    18.     Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"}
    19.     LOD 200
    20.  
    21. CGPROGRAM
    22. #pragma surface surf Lambert alphatest:_Cutoff
    23. #pragma target 3.0
    24.  
    25. sampler2D _MainTex;
    26. sampler2D _Tex2;
    27.  
    28. float _HueShift;
    29. float _SatShift;
    30. float _ValShift;
    31.  
    32. fixed4 _Color;
    33.  
    34. float3 shift_col(float3 RGB, float3 shift)
    35.             {
    36.             float3 RESULT = float3(RGB);
    37.             float VSU = shift.z*shift.y*cos(shift.x*3.14159265/180);
    38.                 float VSW = shift.z*shift.y*sin(shift.x*3.14159265/180);
    39.              
    40.                 RESULT.x = (.299*shift.z+.701*VSU+.168*VSW)*RGB.x
    41.                         + (.587*shift.z-.587*VSU+.330*VSW)*RGB.y
    42.                         + (.114*shift.z-.114*VSU-.497*VSW)*RGB.z;
    43.              
    44.                 RESULT.y = (.299*shift.z-.299*VSU-.328*VSW)*RGB.x
    45.                         + (.587*shift.z+.413*VSU+.035*VSW)*RGB.y
    46.                         + (.114*shift.z-.114*VSU+.292*VSW)*RGB.z;
    47.              
    48.                 RESULT.z = (.299*shift.z-.3*VSU+1.25*VSW)*RGB.x
    49.                         + (.587*shift.z-.588*VSU-1.05*VSW)*RGB.y
    50.                         + (.114*shift.z+.886*VSU-.203*VSW)*RGB.z;
    51.              
    52.             return (RESULT);
    53.             }
    54.  
    55.  
    56. struct Input {
    57.     float2 uv_Tex2;
    58.     float2 uv_MainTex;
    59. };
    60.  
    61. void surf (Input IN, inout SurfaceOutput o)
    62. {
    63.     fixed4 b = tex2D(_Tex2, IN.uv_Tex2) * _Color;
    64.     fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    65.  
    66.     float3 hsv;
    67.     hsv.x  = _HueShift;
    68.     hsv.y  = _SatShift;
    69.     hsv.z  = _ValShift;
    70.  
    71.     c.rgb = shift_col(c.rgb, hsv);
    72.  
    73.     o.Albedo = b.rgb * b.a + c.rbg * c.a * (1-b.a);
    74.     o.Alpha = c.a;
    75.  
    76. }
    77. ENDCG
    78. }
    79.  
    80. Fallback "Transparent/Cutout/VertexLit"
    81. }
    82.  
     
    twobob likes this.
  15. Arkhivrag

    Arkhivrag

    Joined:
    Apr 25, 2012
    Posts:
    2,972

    Just released Texture Adjustments asset including various texture tweaking tools, including hue shift, saturation,contrast, levels control, etc. Plugin comes with shaders, editor tool and runt-time API.



    VacuumShaders - Facebook Twitter YouTube
     
  16. Allan-Smith

    Allan-Smith

    Joined:
    Feb 7, 2012
    Posts:
    57
    10 years later I find this thread as the first result when looking for hue shift shaders... and, standing the test of time, @DtodaB solution works almost perfectly for what I was looking for, which is using this on sprite renderers so I can have only one background texture and recolor it without using multiple huge textures... the one thing I had to change on the shader is that SpriteRenderers use their color to adjust vertex color which the original shader does not consider, therefore if you change the alpha of the color, or a canvas group alpha, this shader would still be solid.

    The change was literally just creating an in struct that receives color, and passing on to the pixel shader, so basically nothing. Here is the updated version to be use hue shift with sprite renderers and canvas groups:

    Code (CSharp):
    1. Shader "Custom/HSVShader" {
    2.     Properties {
    3.         _MainTex ("Texture", 2D) = "white" {}
    4.         _HueShift("HueShift", Float ) = 0
    5.         _Sat("Saturation", Float) = 1
    6.         _Val("Value", Float) = 1
    7.     }
    8.     SubShader {
    9.  
    10.         Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType" = "Transparent" }
    11.         ZWrite Off
    12.         Blend SrcAlpha OneMinusSrcAlpha
    13.         Cull Off
    14.  
    15.         Pass
    16.         {
    17.             CGPROGRAM
    18.             #pragma vertex vert
    19.             #pragma fragment frag
    20.             #pragma target 2.0
    21.  
    22.             #include "UnityCG.cginc"
    23.            
    24.  
    25.  
    26.             float3 shift_col(float3 RGB, float3 shift)
    27.             {
    28.             float3 RESULT = float3(RGB);
    29.             float VSU = shift.z*shift.y*cos(shift.x*3.14159265/180);
    30.                 float VSW = shift.z*shift.y*sin(shift.x*3.14159265/180);
    31.                
    32.                 RESULT.x = (.299*shift.z+.701*VSU+.168*VSW)*RGB.x
    33.                         + (.587*shift.z-.587*VSU+.330*VSW)*RGB.y
    34.                         + (.114*shift.z-.114*VSU-.497*VSW)*RGB.z;
    35.                
    36.                 RESULT.y = (.299*shift.z-.299*VSU-.328*VSW)*RGB.x
    37.                         + (.587*shift.z+.413*VSU+.035*VSW)*RGB.y
    38.                         + (.114*shift.z-.114*VSU+.292*VSW)*RGB.z;
    39.                
    40.                 RESULT.z = (.299*shift.z-.3*VSU+1.25*VSW)*RGB.x
    41.                         + (.587*shift.z-.588*VSU-1.05*VSW)*RGB.y
    42.                         + (.114*shift.z+.886*VSU-.203*VSW)*RGB.z;
    43.                
    44.             return (RESULT);
    45.             }
    46.  
    47.             struct vertex_data
    48.             {
    49.                 float4 vertex   : POSITION;
    50.                 float4 color    : COLOR;
    51.                 float2 texcoord : TEXCOORD0;
    52.             };
    53.  
    54.             struct v2f {
    55.                 float4  pos : SV_POSITION;
    56.                 float2  uv : TEXCOORD0;
    57.                 float4  col : COLOR;
    58.             };
    59.            
    60.             float4 _MainTex_ST;
    61.            
    62.             v2f vert (vertex_data v)
    63.             {
    64.                 v2f o;
    65.                 o.pos = UnityObjectToClipPos (v.vertex);
    66.                 o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    67.                 o.col = v.color;
    68.                 return o;
    69.             }
    70.  
    71.             sampler2D _MainTex;
    72.             float _HueShift;
    73.             float _Sat;
    74.             float _Val;
    75.  
    76.             half4 frag(v2f i) : COLOR
    77.             {
    78.                 half4 col = tex2D(_MainTex, i.uv) * i.col;
    79.                 float3 shift = float3(_HueShift, _Sat, _Val);
    80.                
    81.                 return half4( half3(shift_col(col, shift)), col.a);
    82.             }
    83.             ENDCG
    84.         }
    85.     }
    86.     Fallback "Particles/Alpha Blended"
    87. }
     
    april_4_short likes this.
  17. april_4_short

    april_4_short

    Joined:
    Jul 19, 2021
    Posts:
    489
    Thank you!

    This is most welcome/essential/cool!