Search Unity

Question What is the best way to whiten a texture to apply color variations?

Discussion in 'Shaders' started by carcasanchez, Aug 30, 2021.

  1. carcasanchez

    carcasanchez

    Joined:
    Jul 15, 2018
    Posts:
    177
    Hello everyone! Today I have an inquiry regarding Compute Shaders.
    My intention is to whiten the texture of a character in editor to then apply color variations on runtime:
    upload_2021-8-30_16-25-12.png
    upload_2021-8-30_16-25-20.png

    The problem, however, is that I cannot find any shader I am comfortable with.
    What I am using right now is a combination of a grayscale + gradient computer shaders, but there are some noisy artifacts.
    upload_2021-8-30_16-25-30.png

    Since i'm not a shader expert, I'm not sure what is the best option to do the effect I'm searching for (keep the detail of the texture while cleaning the main color). I'm not looking to a perfect result, but I would like to achieve at least clean colors. Maybe something like an edge-detecting shader?
    Anyway, here's what I'm currently using:

    Grayscaling
    Code (CSharp):
    1.  
    2. #pragma kernel CSMain
    3.  
    4. // ( CPU -> GPU )
    5. Texture2D inputTexture;
    6.  
    7. // ( GPU -> CPU )
    8. RWTexture2D <float4> outputTexture;
    9.  
    10. int textWidth;
    11. int textHeight;
    12.  
    13. float Luminance(float3 c)
    14. {
    15.    return dot(c, float3(0.22, 0.707, 0.071));
    16. }
    17.  
    18. [numthreads(8, 8, 1)]
    19. void CSMain(uint3 id : SV_DispatchThreadID)
    20. {
    21.    float R = dot(inputTexture[id.xy].r, float3(0.22, 0.707, 0.071)) ;
    22.    float G = dot(inputTexture[id.xy].g, float3(0.22, 0.707, 0.071));
    23.    float B = dot(inputTexture[id.xy].b, float3(0.22, 0.707, 0.071));
    24.  
    25.    outputTexture[id.xy] = float4(R,G,B, 1);
    26. }
    27.  
    28.  

    Grading
    Code (CSharp):
    1. #pragma kernel CSMain
    2.  
    3. // ( CPU -> GPU )
    4. Texture2D inputTexture;
    5.  
    6. // ( GPU -> CPU )
    7. RWTexture2D <float4> outputTexture;
    8.  
    9. int textWidth;
    10. int textHeight;
    11. float gradientStrenght;
    12.  
    13. [numthreads(8, 8, 1)]
    14. void CSMain(uint3 id : SV_DispatchThreadID)
    15. {
    16.     float4 outputColor;
    17.  
    18.     if (id.x == 0 || id.y == 0 || id.x == textWidth - 1 || id.y == textHeight - 1)
    19.     {
    20.         outputColor = float4(0, 0, 0, 1);
    21.     }
    22.     else
    23.     {
    24.         float3 c = inputTexture[float2(id.x - 1, id.y - 1)];
    25.         float3 cSampleNegXNegY = float3(c.r, c.g, c.g);
    26.         c = inputTexture[float2(id.x, id.y - 1)];
    27.         float3 cSampleZerXNegY = float3(c.r, c.g, c.g);
    28.         c = inputTexture[float2(id.x + 1, id.y - 1)];
    29.         float3 cSamplePosXNegY = float3(c.r, c.g, c.g);
    30.         c = inputTexture[float2(id.x - 1, id.y)];
    31.         float3 cSampleNegXZerY = float3(c.r, c.g, c.g);
    32.         c = inputTexture[float2(id.x + 1, id.y)];
    33.         float3 cSamplePosXZerY = float3(c.r, c.g, c.g);
    34.         c = inputTexture[float2(id.x - 1, id.y + 1)];
    35.         float3 cSampleNegXPosY = float3(c.r, c.g, c.g);
    36.         c = inputTexture[float2(id.x, id.y + 1)];
    37.         float3 cSampleZerXPosY = float3(c.r, c.g, c.g);
    38.         c = inputTexture[float2(id.x + 1, id.y + 1)];
    39.         float3 cSamplePosXPosY = float3(c.r, c.g, c.g);
    40.  
    41.         float fSampleNegXNegY = cSampleNegXNegY.x;
    42.         float fSampleZerXNegY = cSampleZerXNegY.x;
    43.         float fSamplePosXNegY = cSamplePosXNegY.x;
    44.         float fSampleNegXZerY = cSampleNegXZerY.x;
    45.         float fSamplePosXZerY = cSamplePosXZerY.x;
    46.         float fSampleNegXPosY = cSampleNegXPosY.x;
    47.         float fSampleZerXPosY = cSampleZerXPosY.x;
    48.         float fSamplePosXPosY = cSamplePosXPosY.x;
    49.  
    50.        float edgeX = (fSampleNegXNegY - fSamplePosXNegY) * 0.25 + (fSampleNegXZerY - fSamplePosXZerY) * 0.5 + (fSampleNegXPosY - fSamplePosXPosY) * 0.25;
    51.         float edgeY = (fSampleNegXNegY - fSampleNegXPosY) * 0.25 + (fSampleZerXNegY - fSampleZerXPosY) * 0.5 + (fSamplePosXNegY - fSamplePosXPosY) * 0.25;
    52.  
    53.         float fValue = (edgeX + edgeY) / 2.0f;
    54.         fValue = 1 - (fValue / gradientStrenght);
    55.  
    56.         outputColor = float4(fValue, fValue, fValue, 1.0);
    57.     }
    58.  
    59.     outputTexture[id.xy] = outputColor;
    60. }
    61.  
     
  2. carcasanchez

    carcasanchez

    Joined:
    Jul 15, 2018
    Posts:
    177
    Hi again,
    I have found an interesting HSCB shader that does a very good job at grayscaling.
    upload_2021-8-31_14-45-22.png
    However, when applying a color over this texture, since there are parts that are dark grey, the tend to gravitate towards black, constraining the palette. This also cause problems when the original textures have different values.
    I tried to use the Grade shader i posted above. It does a good job balancing greys, but causes finer details to dilute. (Left is original model)
    upload_2021-8-31_14-48-35.png

    I have been investigating, but didn't found anything. Is there a formula to pick those middle greys and turn them towards white, while keeping the contrast needed to maintain details? Doesn't need to be perfect, just enough to distinguish facial traits.
     
  3. Sinterklaas

    Sinterklaas

    Joined:
    Jun 6, 2018
    Posts:
    93
    The easiest way that I can think of, is have your shirt be completely grayscale to begin with. Then it's just a simple color multiplication to get a differently-colored shirt. Alternatively, you could use a hue shift algorithm.

    Of course, to avoid recoloring the rest of your character, you'd have to use a simple black-and-white texture that tells your shader which parts of your texture need to be recolored, and which parts should stay the same. If you don't understand what I mean by that, let me know and I'll give you a visual example.

    Or you could split your object into different parts, where the shirt has its own material, but that's probably more work.