Search Unity

Green Screen Shader

Discussion in 'Shaders' started by prismspecs, Jul 21, 2015.

  1. prismspecs

    prismspecs

    Joined:
    May 28, 2015
    Posts:
    7
    There are a bunch of posts about this from a few years back, but most of them no longer work with the latest version of Unity or are imperfect. This is the best one I found:
    Code (csharp):
    1.  
    2. Shader"Custom/ChromaKey" {
    3.  
    4. Properties {
    5. _MainTex ("Base (RGB)", 2D) = "white" {}
    6. _AlphaValue ("AlphaValue", Range(0.0,1.0)) = 1.0
    7.  }
    8.  
    9. SubShader {
    10. Tags { "Queue"="Transparent""RenderType"="Transparent" }
    11. LOD200
    12.  
    13. CGPROGRAM
    14. #pragma surface surfLambert alpha
    15.  
    16. sampler2D_MainTex;
    17. float_AlphaValue;
    18.  
    19. structInput {
    20. float2uv_MainTex;
    21.  };
    22.  
    23. voidsurf (InputIN, inoutSurfaceOutputo) {
    24. half4c = tex2D (_MainTex, IN.uv_MainTex);
    25. o.Emission = c.rgb;
    26.  
    27. //Greenscreenlevel - leavesminorgreenglow
    28. if (c.g >= 0.67f && c.r <= 0.65f && c.b <= 0.65f && _AlphaValue == 1.0)
    29.  {
    30. o.Alpha = 0.0;
    31.  }
    32. else
    33.  {
    34. o.Alpha = 1.0;
    35.  }
    36.  
    37.  }
    38. ENDCG
    39.  }
    40. FallBack"Diffuse"
    41.  
    The only problem with this is that it makes the background a semi-transparent green rather than just not rendering it. Ideas?
     

    Attached Files:

    Last edited: Jul 21, 2015
  2. MSplitz-PsychoK

    MSplitz-PsychoK

    Joined:
    May 16, 2015
    Posts:
    1,278
    There may be more going wrong that I'm not noticing, but you're using a surface shader with default (lambert) lighting. For the parts of the greenscreen that should not render, you set their alpha to 0 (o.Alpha = 0), so you're attempting to not render that pixel. However, once you reach the lighting stage, your pixel with 0 alpha gets lit and now has alpha!

    to prevent this, replace "o.Alpha = 0.0;" with "discard;"



    Ideally, you don't want lighting to affect this surface, but that's not an option with surface shaders. (you can make your own lighting pass that doesn't light the surface, but it never works 100% the way you want it to) I recommend settling with the above solution, or trying to switch this over to a vertex-fragment shader.
     
  3. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    I would also not go for a surface shader, but a vertex/fragment instead.

    Besides that I'm not a big fan of this line:
    Code (csharp):
    1. if (c.g >= 0.67f && c.r <= 0.65f && c.b <= 0.65f && _AlphaValue == 1.0)
    So the definition of a green screen here is that green is above 0.67 and red and blue are below 0.65?

    I wouldn't switch alpha between 0 and 1, but actually make a smooth transition if colors are close to being the green screen. You could do that by simply taking the distance between the current color and the target green screen color.

    Then I would think that the brightness of the green screen should not matter. You can convert both the current color and the green screen target to yuv space or something similar and only take the distance between the uv color components.

    By then you'll end up with a completely different shader, but I think it will work better as a green screen shader than that simple if.
     
  4. MSplitz-PsychoK

    MSplitz-PsychoK

    Joined:
    May 16, 2015
    Posts:
    1,278
    Actually another thought on this line... shader code is meant to be able to run on several GPU cores at the same time. Math and other general graphics operations can be done easily by multiple cores at a time, but "if" checks require a single core to focus on making a decision before all the cores can continue their job. Because of this, having that huge "if" statement in a shader can slow down your graphics. There's a good chance this won't have that bad of an effect on you, but if you notice your framerate drop on the GPU, watch out for those "if" statements.
     
  5. Plutoman

    Plutoman

    Joined:
    May 24, 2013
    Posts:
    257
    I just need to correct this here.. what happens with if statements is that it processes pixels in groups, batches, and runs the same code for each batch. If there's if statements, it will take both branches and then discard results depending on the branching essentially by using step statements to take one or the other result.

    So what happens in if statements is that if the variance happens across lots of difference pixels, it'll run all the code. It's not all that critical to 'avoid' them, but the catch is you cannot optimize by using them without a dynamic branch (which has an implicit cost and is only in newer GPU's).

    While I agree with jvo3dc on his comments, the if statement will not make a difference here in all but the lowest end GPU's since it's adding just a few ALU operations.
     
  6. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    You're absolutely right Plutoman. As a general rule I still try to avoid if statements in shaders anyway. If it ends up on low end (=mobile) GPU's you could essentially get the smooth transition for free. The if will be converted to something like:
    Code (csharp):
    1.  
    2. result = lerp(a, b, step(c, d));
    3.  
    Which has roughly the same costs as something like:
    Code (csharp):
    1.  
    2. result = lerp(a, b, saturate((c - d) / e));
    3.  
    Besides that, I think you really want that smooth transition in almost all cases. And the case of the green screen is no exception I think. All cases of color replacement really. If statements on floating point representations of colors are just not the way to go in my opinion.