Search Unity

Closest Color Shader

Discussion in 'Shaders' started by DawdleDev, Apr 26, 2019.

  1. DawdleDev

    DawdleDev

    Joined:
    Aug 30, 2017
    Posts:
    10
    I am trying to create a shader that takes two input textures, one being an ordinary image and the other a color pallete containing a horizontal list of colors. Then, it goes through and replaces all pixels of the first image with the closest color match on the pallete image.

    Essentially, the shader would do the following:

    Input image:
    Input1.png

    Color Pallete (scaled up, real pallete is always 1 pixel tall)
    Input2.png

    Shader output
    Output.png

    Obviously, the shader would have a more comprehensive color pallete and a more interesting image, but this just shows the concept.

    Now, here's my question. How do I compare the color of the texture fragment to each color in the pallete? And how do I compare them in a way that they visually appear similar (IE a human would say the shader picked the best possible color)? Assume alpha isn't used in either texture, just RGB. I am using the latest Unity version, and am trying to learn shader code. Shader graph examples would help, but I would prefer examples using CG code. Also, this is a post effect, and has to be. Just coloring models won't account for the lighting in the scene.
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    There are two ways to go about doing a strict palette like this. There's the brute force way, and the smarter (but more approximate) way.

    The brute force method is you pass a list of colors to your shader, either as a swatch like above, or as an array, then compare the current pixel to that color to determine the "distance", choosing the closest color. There are a lot of ways to determine the distance, most are done by converting to a different color space, but distance(input.rgb, swatch.rgb) is probably going to be good enough much of the time.
    https://github.com/oxysoft/RetroSuite3D/blob/master/Assets/Shaders/Custom/RetroPixelMax.shader

    The smarter way is by using a 3D look up texture, or LUT, like how most post process color grading is done. Basically you need to take the default LUT texture that comes with the post processing stack, palletize it in your application of choice, then import it back in. This takes the 2D texture your import and builds a 3D texture to use as a LUT in a fairly simple shader. The issue here is that using the post processing stack for this applies to the whole screen, which you may not want. If you want multiple palettes it means a lot of 3D LUTs which get expensive memory wise. And the 3D textures that the post processing stack creates using linear filtering so the colors might not be only those you've selected but also some in between values. If you want to ensure only those colors you want are used, you'd have to write the c# code to generate the 3d textures yourself and set them to be point filtered, and the post processing effect or per object shader that uses that 3d texture.


    There are also assets on the store that can do this for you. I don't know what method they use, but here's a random example:
    https://assetstore.unity.com/packages/vfx/shaders/fullscreen-camera-effects/stylizer-basic-93677
     
  3. DawdleDev

    DawdleDev

    Joined:
    Aug 30, 2017
    Posts:
    10
    Thanks! The first answer works for me (I want brute force for this application), and the second was pretty useful.
     
  4. perholmes

    perholmes

    Joined:
    Dec 29, 2017
    Posts:
    296
    You give some extremely high quality answers, thanks for your effort around the forum.
     
  5. Shadestyle

    Shadestyle

    Joined:
    Nov 23, 2019
    Posts:
    1
    Are there any resources you could provide to explain how someone might create a 3D texture in scripting, in the way you described to get the ensured hardset colors?