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

Transparent shader that makes objects behind it greyscale

Discussion in 'Shaders' started by David Colson, Aug 1, 2013.

  1. David Colson

    David Colson

    Joined:
    Jul 29, 2013
    Posts:
    3
    Hello,

    My descriptive title hopefully caught your attention. I have no experience with shaderlab whatsoever (or shading at all really) so I am using SSE to help me.

    I want an transparent object that effects objects behind it. So I can make part of the screen greyscale using a volume with the shader on it. Am I wasting in my time with this? Is it even possible? I don't unity pro so I don't have the render texture features.

    If this is possible, or if there is a better way to achieve the effect I want could you please offer your assistance?

    David
     
  2. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    Without GrabPass or RenderTextures (both Pro features), changing the background with a shader isn't really possible.
     
  3. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    In order to influence pixels that are already drawn to the screen, the only option you have is to use blending, or render textures. With blending, you can do a conversion to grayscale by making your blend mode multiply a source texture or fixed color by the screen. It would be like `Blend DstColor None`. What that translates to is to multiply the source RGB by the RGB in the screen, and that's all. Your source texture can either be an RGB texture containing color weights for each channel (ie to convert color to grayscale you usually multiply each channel by a special number), OR you can just use a fixed color containing those values. So a very extremely simple shader is needed where you simply output a constant color, and use Blend DstColor None to apply it as grayscale to the background. Using a source texture instead would let you vary `how grayscale` pixels are on a per-pixel basis, if you want that much control.

    For the color values to use to multiply against the backbuffer, try Red=0.2125, Green=0.7154, Blue=0.0721

    In terms of transparency I don't know if you have a wish to have semi-transparency at the same time as converting to grayscale but you can't do that. What you can do is an all-or-nothing conversion to grayscale wherever you draw pixels.
     
    Last edited: Aug 1, 2013
  4. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    This is a good idea I hadn't thought of. With this approach, you could have a two-pass shader that first desaturates the background and then draws whatever you want on top.
     
  5. David Colson

    David Colson

    Joined:
    Jul 29, 2013
    Posts:
    3
    Well this sounds good, and am I still good to do this without render textures?
     
  6. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
  7. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    Nothing, I don't think. Human just conflated component-wise multiplication with the dot product, and I didn't think it through.
     
  8. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    One other possibility is to render the greyscale version of each render into its alpha channel. Then, another object could reveal that alpha channel by rendering pure white with Blend DstAlpha Zero. (There are some other blend operations that would yield the same result, too.)

    Alpha blended objects would need to use an extra pass, however.

    The first one would be like this:
    Blend SrcAlpha OneMinusSrcAlpha, Zero OneMinusSrcAlpha
    (You need the alpha channel to act as a mask on the background, as per usual.)

    Then you'd premultiply the greyscale alpha channel and use this:
    Blend Zero One, One One

    That's going to be complicated if you're using all of your own shaders, and impossible otherwise.
     
  9. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    I havent actually coded it to see if it works but basically isn't grayscale conversion as simple as multiplying each color component by a weight value? So then basically all you need is a shader that outputs a color (or texture) and then a blend mode that simply multiplies that color by what's in the backbuffer. Blend DstColor None does that.

    It would completely grayscale-ize the backbuffer colors, yes... mmm.. you could partially grayscale them by adjusting the weights, because weights of 1.0,1.0,1.0 apply no grayscale at all. But I don't think you can simultaneously output another semi-transparent texture on top in the same pass.
     
  10. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    That's the first part. Then you add the weighted values together, and use the result in all channels. Like I said, otherwise you just make something green*. I think of a color multiply as using colored cellophane, or dye; it's a simple filter.

    The limitation of non-mobile GPUs which makes it impossible to use blend modes for desaturation is that you can't swizzle color channels. You can get the alpha channel into any combination of the other channels, but you can't get any of the other channels into alpha.

    I feel bad about overwhelming David, but you don't have to just rely on full-on desaturation, using the last method I described. You can multiply whatever you want into the desaturated data in the color buffer's alpha channel, and alpha blend it, using one pass:

    Render whatever you want to multiply in, premultiplying its alpha blend in the fragment shader.
    Use Blend DstAlpha OneMinusSrcAlpha.
    If you use white in all four channels, it'll be the same as what I described above.


    * Recently, I did a multiply by high values in green and blue, fading to medium-high values only in blue. A lot of people think using multiplies on walnut results in too low of color values, but I dig it, especially with the contrast you get against filtered maple. Hard Rock Maple is the closest wood we get to (1,1,1).
     
    Last edited: Aug 2, 2013
  11. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    Ah okay, I'm with you now re the adding together part. Hmm yes, problemmo doing that at all with blending.