Search Unity

Having projectors work with cutout sprites

Discussion in 'Editor & General Support' started by dkollmann, Mar 29, 2012.

  1. dkollmann

    dkollmann

    Joined:
    Jul 25, 2009
    Posts:
    25
    The way I understand it, this would require that when the projector is rendered it has access to the main texture set on the object so its alpha could be multiplied into the alpha of the projector.

    Is there any way to access the original main texture an object has during the projector rendering?
     
  2. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,327
    No. Why would it matter? Have you tried this and found that you don't get the results you want?
     
  3. dkollmann

    dkollmann

    Joined:
    Jul 25, 2009
    Posts:
    25
    What I try to do is that I have a plane as a sprite in a 2D game. This means that big areas of the texture are fully transparent as the character only occupies a part of the actual texture. Now when I apply a projector to it, the previously invisible parts become visible again. This behaviour is what I would expect from a projector. I just need to find a way to fix this.

    I played a bit with the previous value in the shaderlab but could not get the desired results.

    Another idea I had was to use alphatest with zwrite and then see if I can use this information, like "only render when previous.z <= current.z".

    I would prefer to not use render targets as I want to have this working on mobile devices with multiple characters that all use a projector to generate their shadow.

    I was also wondering if I could use the components PreRender method to change the texture on the projector but that would probably require that I find the current renderqueue I am in, as projectors are in Transparent -1. I already changed this to Transparent +1 so the shadow projects onto the transparent objects and of course I set IgnoreProjectors to false.

    I don't know if there is an easy way to have your own projector class as projectors seem to be sort of "hacked" into the render queue anyway.

    I was hoping that someone could point out a way that is most promising that this could actually be achieved.
     
  4. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,327
    You should not expect that. A more appropriate ZTest mode for projectors is Equal, not LEqual.

    <= won't respect cutouts. == will.
     
    Last edited: Mar 29, 2012
  5. dkollmann

    dkollmann

    Joined:
    Jul 25, 2009
    Posts:
    25
    Thanks for your help. ztest equal did the trick.
     
  6. Merman

    Merman

    Joined:
    Nov 16, 2014
    Posts:
    51
    Hello @dkollmann and @Jessy.
    I was wondering if either of you could explain this in greater detail. I am also trying to get projectors to work with alpha test/cutout shader materials. So far, everything I have tried has failed and I haven't been able to find much information on this subject.

    Thank you both in advance for any assistance you're able to provide.
     
  7. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,327
    Why has ZTest Equal failed? What constitutes failure?
     
    Last edited: Dec 19, 2014
  8. Merman

    Merman

    Joined:
    Nov 16, 2014
    Posts:
    51
    It most likely failed because I don't know the proper place to use it.
    Taking the Projector Multiply shader from Unity's Standard Assets for example, the API makes me think I should insert it near the beginning of the pass, as follows:
    Code (CSharp):
    1. Shader "Projector/Multiply" {
    2.     Properties {
    3.         _ShadowTex ("Cookie", 2D) = "gray" {}
    4.         _FalloffTex ("FallOff", 2D) = "white" {}
    5.     }
    6.     Subshader {
    7.         Tags {"Queue"="Transparent"}
    8.         Pass {
    9.             ZWrite Off
    10.             Ztest Equal
    11.             Fog { Color (1, 1, 1) }
    12.             AlphaTest Greater 0
    13.             ColorMask RGB
    14. //additional shader code below ...
    But when I add that line (Ztest Equal) the projector simply stops showing up completely (as if there is no projector present, at all). Different ZTest comparisons also have unexpected results. So far "failure" has meant that I either A) break the shader (meaning the editor displays the Fuchsia Absence Nightmare material, as I've started calling it -- this is when the shader or material is missing or damaged and everything affected turns 80s neon electric fuchsia), or B) get the projector looking good, but still projecting on areas that should be invisible (which is what I'm trying to stop).

    I've tried changing the render queue (Transparent+1 or Transparent-1, for example) to no avail.
    I've tried removing the line "AlphaTest Greater 0" completely, as well as just changing the AlphaTest comparison to other things (I went through each comparison mode listed in the API). None of it has caused the projector to be invisible where the material it is projecting onto is transparent, except for combinations that caused the projector to be entirely invisible (again, as if there was no projector).

    Here is a link to the thread I started, hoping someone could point me in the right direction (not that there is much more information there). Hopefully I've explained myself coherently.
    http://forum.unity3d.com/threads/projectors-only-where-opaque-projecting-on-cutouts.285172/
     
  9. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,327
    Are you using deferred rendering? In forward rendering, a projector's effects are going to be visible if you test for depth equality on an object that was already rendered with cutouts. If you're not seeing it, it means your other object wasn't rendered already, or it didn't write to the depth buffer. Cutout objects belong in or around the "AlphaTest" queue, and should write depth.
     
  10. Merman

    Merman

    Joined:
    Nov 16, 2014
    Posts:
    51
    Thank you for your help. It looks like the problem may actually be with my cutout shader and not necessarily with my projector shaders:

    I am using forward rendering. My cutout shader is in the AlphaTest queue with Zwrite on (the projector shaders are in the Transparent queue). Turning on Zwrite in the cutout shader definitely made a big difference. The transparent areas are much harder to see now ... but they are still slightly visible -- not because the projector betrays them, but because the absence of the projector now betrays them. What I mean is that the projector no longer projects onto the transparent parts of the cutout (which is exactly what I want), but now projectors also can't be seen behind those transparent areas. For example, if a cutout moves in front of a surface that is being affected by a projector, the projector's effects on that other object are hidden (i.e. the original, unaffected color becomes visible) wherever the passing cutout is transparent.

    Hopefully this image will explain better:





    Additional note:
    It isn't shown above, but turning on ZWrite in the cutout shader and using ZTest Equal in the projector shader (whether ZWrite is on or off there) has the visual effect of simply disabling the projector from the Inspector.
     
    Last edited: Dec 27, 2014
  11. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,327
    That seems clear enough, but that doesn't sound like a "cutout" shader anymore. It sounds like you're blending instead of alpha testing.
     
  12. Merman

    Merman

    Joined:
    Nov 16, 2014
    Posts:
    51
    What I am calling a "cutout shader" is based on the default resources shader found in Transparent/Cutout/Soft Edge Unlit:

    Code (CSharp):
    1. Shader "Transparent/Cutout/Soft Edge Unlit" {
    2. Properties {
    3.     _Color ("Main Color", Color) = (1, 1, 1, 1)
    4.     _MainTex ("Base (RGB) Alpha (A)", 2D) = "white" {}
    5.     _Cutoff ("Base Alpha cutoff", Range (0,.9)) = .5
    6. }
    7.  
    8. SubShader {
    9.     Tags { "Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout" }
    10.     Lighting off
    11.    
    12.     // Render both front and back facing polygons.
    13.     Cull Off
    14.    
    15.     // first pass:
    16.     //   render any pixels that are more than [_Cutoff] opaque
    17.     Pass {
    18.         CGPROGRAM
    19.             #pragma vertex vert
    20.             #pragma fragment frag
    21.            
    22.             #include "UnityCG.cginc"
    23.  
    24.             struct appdata_t {
    25.                 float4 vertex : POSITION;
    26.                 float4 color : COLOR;
    27.                 float2 texcoord : TEXCOORD0;
    28.             };
    29.  
    30.             struct v2f {
    31.                 float4 vertex : SV_POSITION;
    32.                 float4 color : COLOR;
    33.                 float2 texcoord : TEXCOORD0;
    34.             };
    35.  
    36.             sampler2D _MainTex;
    37.             float4 _MainTex_ST;
    38.             float _Cutoff;
    39.            
    40.             v2f vert (appdata_t v)
    41.             {
    42.                 v2f o;
    43.                 o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
    44.                 o.color = v.color;
    45.                 o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
    46.                 return o;
    47.             }
    48.            
    49.             float4 _Color;
    50.             half4 frag (v2f i) : SV_Target
    51.             {
    52.                 half4 col = _Color * tex2D(_MainTex, i.texcoord);
    53.                 clip(col.a - _Cutoff);
    54.                 return col;
    55.             }
    56.         ENDCG
    57.     }
    58.  
    59.     // Second pass:
    60.     //   render the semitransparent details.
    61.     Pass {
    62.         Tags { "RequireOption" = "SoftVegetation" }
    63.        
    64.         // Dont write to the depth buffer
    65.         ZWrite off
    66.        
    67.         // Set up alpha blending
    68.         Blend SrcAlpha OneMinusSrcAlpha
    69.        
    70.         CGPROGRAM
    71.             #pragma vertex vert
    72.             #pragma fragment frag
    73.            
    74.             #include "UnityCG.cginc"
    75.  
    76.             struct appdata_t {
    77.                 float4 vertex : POSITION;
    78.                 float4 color : COLOR;
    79.                 float2 texcoord : TEXCOORD0;
    80.             };
    81.  
    82.             struct v2f {
    83.                 float4 vertex : SV_POSITION;
    84.                 float4 color : COLOR;
    85.                 float2 texcoord : TEXCOORD0;
    86.             };
    87.  
    88.             sampler2D _MainTex;
    89.             float4 _MainTex_ST;
    90.             float _Cutoff;
    91.            
    92.             v2f vert (appdata_t v)
    93.             {
    94.                 v2f o;
    95.                 o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
    96.                 o.color = v.color;
    97.                 o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
    98.                 return o;
    99.             }
    100.            
    101.             float4 _Color;
    102.             half4 frag (v2f i) : SV_Target
    103.             {
    104.                 half4 col = _Color * tex2D(_MainTex, i.texcoord);
    105.                 clip(-(col.a - _Cutoff));
    106.                 return col;
    107.             }
    108.         ENDCG
    109.     }
    110. }
    111.  
    112. }
    In this unmodified version, you can see that the blending was already part of the second pass.
     
  13. Merman

    Merman

    Joined:
    Nov 16, 2014
    Posts:
    51
    Would anyone else care to chime in?
     
  14. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,327
    Sorry, I don't think I ever saw your screenshot. I'm not 100% clear on your desires, but I think what you you want is to split two passes into two materials. You could just render both passes right before or after the projector, but it will be inefficient and won't batch.
    1. "Queue"="AlphaTest" "IgnoreProjector"="True" ZWrite On
    2. "Queue"="Transparent" "IgnoreProjector"="True" ZWrite Off Blend SrcAlpha OneMinusSrcAlpha
    Regardless of what you want, take it one pass at a time. Two passes without lots of experience is too complicated. The optimal challenge/skill ratio, according to my recent research, is 104%.
     
    Last edited: Jan 15, 2015
  15. Merman

    Merman

    Joined:
    Nov 16, 2014
    Posts:
    51

    Hello again, Jessy.

    I'm fine with working with just one pass. The only reason there are two is because, as I said, the shader was based on one of the default shaders that came prepackaged with Unity ("Transparent/Cutout/Soft Edge Unlit").

    What I am trying to do is get a projector to modify the appearance of an object (as in my third screenshot above) but with the transparent edges remaining transparent instead of taking on the appearance of the projector (as in my second screenshot above). If this can be done in one pass, I'm all for it.

    I want to use a cutout shader (such as the default versions that come with Unity) and have it interact with a projector, while still remaining "cut out" from its transparent edges. Basically, if I have an object using a cutout shader to make certain parts of its texture completely transparent, using a projector on it should affect only the areas that are opaque and not the areas that are transparent (because that would defeat the purpose of using cutout shaders).
     
  16. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,327
    It sounds like you want to use one pass and write depth in that pass. But if it's that simple, I don't know why we would have been at it for a couple months... :confused:
     
  17. wolfulus

    wolfulus

    Joined:
    Jun 10, 2015
    Posts:
    6
    Last edited: Jul 31, 2016
  18. Merman

    Merman

    Joined:
    Nov 16, 2014
    Posts:
    51

    I'm sorry, @wolfulus , I never did manage to figure out a solution. Actually what you see in this thread is pretty much as far as I got with it. I still think it's very strange that this issue isn't more common than it seems to be just based on my impression that so-called "cutout shaders" are a very common way to avoid modeling additional geometry (and even more common when it comes to sprites in 2D games). I'm personally more of an artist than a programmer, though. So while I know this can be accomplished, I was at a loss when it came to figuring out how.
     
  19. BJSDA_Brian

    BJSDA_Brian

    Joined:
    May 17, 2014
    Posts:
    3
    In the projector shader, inside the Pass { } near the top, change "Offset -1, -1" to "Offset 0,0" and add the line "Ztest Equal". If memory serves, that should get the projector to show up on an alpha clipped object and respect the clipping, at least in deferred rendering.
     
    Nelson-William and Unknown654353 like this.
  20. Unknown654353

    Unknown654353

    Joined:
    Oct 26, 2017
    Posts:
    1
    :):):) SOLVED THANKS !!! :):):):)
     
  21. FeastSC2

    FeastSC2

    Joined:
    Sep 30, 2016
    Posts:
    598
    I'm having trouble with projectors and sprites too. Could you share how you managed to make it work?

    The dropbox link is down.

    I'm very interested in figuring this out, any help is greatly appreciated~!