Search Unity

fullscreen shader / copy the color under the darkest pixel

Discussion in 'Shaders' started by og0, Jan 26, 2015.

  1. og0

    og0

    Joined:
    Jun 13, 2013
    Posts:
    4
    Hi all,

    I'm a newbie in shaders. I'm working on a grayscale image. I'd like to write a shaders which, for each column of pixels, finds the darkest pixel and colorize each pixel under it with the same color. Can i do it with shaders ? How can i scan each column of pixels, save the darkest color and know the pixel coordinates ?

    Thanks a lot !
     
  2. TechnoCraft

    TechnoCraft

    Joined:
    Apr 6, 2012
    Posts:
    28
    This is an advanced topic. The point is you need a really good reason to do that. I would advise you to read more about the basics before returning to this problem. See this tutorial series from Unity Cookie.

    You are asking: How do I scan a specifis pixel color from a texture inside a shader function (vert, frag).
    You can read a precise color value from a specific point in a texture but some prerequisites must be met first:

    Texture settings:
    Wrap mode: Clamp (While clamp is not really required it will show you when you get out of scope with the coordinates)
    Filter mode: Point (If you need exact "pixel-perfect" colors this is a must. Otherwise the colors will be blended based on selected filter).

    Let's assume the texture size is 32x32.
    Indexes start at 0 and end at length-1. "Valid" coordinates from [0..31]. By valid i refer to "clamp-ed" texture. If you specify index out of these bounds you will get the closest border color from the texture. If "Repeat" wrap mode is used then the texture is tiled ("into infinity"). The index is divided with texture size, what remains is the index used to extract the color.

    float height = 32.0 - 1.0;
    float x = 0.0;
    float y = 0.0;

    And you want to get the bottom left corner color:

    float4 color = tex2D(MainTexture, float2(x / height, y / height));

    Top right corner color:
    x = 31.0;
    y = 31.0;
    color = tex2D(MainTexture, float2(x / height, y / height));

    Top "center" color:
    x = 15.0;
    y = 31.0;
    color = tex2D(MainTexture, float2(x / height, y / height));

    And so on ...

    See a bit advanced example when you get the basics on Unity Answers.
     
  3. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    If the height is large enough, you would want to apply a divide and conquer approach. This makes things a bit more complicated, but reduces the running order from O(n) to O(log(n)).

    You'd have to store both the color and the index in this case. As long as the height is 256 or lower, the alpha would be a good place to store the index.

    So, lets say the height is 8. Then in step 1, you reduce the height to 4. You compare index 0 with 4, 1 with 5, 2 with 6 and 3 with 7. In the resulting 4 high texture you store the index and color of the darkest color of the two values compared.

    After that, you can continue this same process for a reduction to 2 and 1 high. Lets take an 8 high example:

    0: 45
    1: 254
    2: 18
    3: 139
    4: 230
    5: 39
    6: 57
    7: 112

    After pass 1 this reduces to:
    0: 45@0 (Lowest of 0 and 4)
    1: 39@5 (Lowest of 1 and 5)
    2: 18@2 (Lowest of 2 and 6)
    3: 112@7 (Lowest of 3 and 7)

    After pass 2 this becomes:
    0: 18@2
    1: 39@5

    And after the final pass:
    0: 18@2

    I just used a single value here instead of a color for simplicity.

    When it comes to indexing, things are actually a bit different. You don't divide by 31 if the height is 32. You divide by 32. If you're not on DX10+, you'll also have to add a half pixel offset. So it's (y + 0.5) / height instead of y / (height - 1).