Search Unity

  1. If you have experience with import & exporting custom (.unitypackage) packages, please help complete a survey (open until May 15, 2024).
    Dismiss Notice
  2. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice

Projector shader to project imagery. going out of bounds?

Discussion in 'Shaders' started by tomatosource, Apr 7, 2015.

  1. tomatosource

    tomatosource

    Joined:
    Dec 12, 2014
    Posts:
    12
    Hi - So I'm trying to create a projector that simply projects an image, in this case, on to terrain.


    So as seen, I've got the textures wrap type set to clamp. The image is the correct size, but it's then using the clamp thingo and stretching the border pixels across the terrain. Eventually I'm planning on loading textures via WWW so am trying to avoid the border of transparent pixels if possible.

    I'm using the following shader I've hacked together -

    Code (CSharp):
    1. Shader "Projector/Texture" {
    2.   Properties {
    3.      _ShadowTex ("Base (RGB)", 2D) = "" { TexGen ObjectLinear }
    4.   }
    5.   Subshader {
    6.       Tags { "RenderType"="Opaque"}
    7.         Pass {
    8.             CGPROGRAM
    9.             #pragma vertex vert
    10.             #pragma fragment frag
    11.             #include "UnityCG.cginc"
    12.            
    13.             struct v2f {
    14.                 float4 uvShadow : TEXCOORD0;
    15.                 float4 pos : SV_POSITION;
    16.             };
    17.            
    18.             float4x4 _Projector;
    19.             float4x4 _ProjectorClip;
    20.            
    21.             v2f vert (float4 vertex : POSITION)
    22.             {
    23.                 v2f o;
    24.                 o.pos = mul (UNITY_MATRIX_MVP, vertex);
    25.                 o.uvShadow = mul (_Projector, vertex);
    26.                 return o;
    27.             }
    28.            
    29.             sampler2D _ShadowTex;
    30.             sampler2D _FalloffTex;
    31.            
    32.             fixed4 frag (v2f i) : SV_Target
    33.             {
    34.                 fixed4 texS = tex2Dproj (_ShadowTex, UNITY_PROJ_COORD(i.uvShadow));
    35.                 texS.a = 1.0-texS.a;
    36.                 return texS;
    37.             }
    38.             ENDCG
    39.         }
    40.    }
    41. }
    Anyone have any ideas on how to fix this so it just projects inside the projectors frustum?


    Thanks for any help!
     
  2. cowtrix

    cowtrix

    Joined:
    Oct 23, 2012
    Posts:
    322
    Hey tomatosource. This is a problem I solved a while ago, by checking the bounds of the uv coordinates before drawing the fragment.

    Something like:
    Code (CSharp):
    1. bool isInClip = input.uvShadow.w > 0.0 && input.uvShadow.x > 0 && input.uvShadow.x < 1 && input.uvShadow.y > 0 && input.uvShadow.y < 1;
    ...and then
    Code (CSharp):
    1. texS.a = (1.0-texS.a) * isInClip;
     
  3. tomatosource

    tomatosource

    Joined:
    Dec 12, 2014
    Posts:
    12
    Thanks heaps for reply, definitely sounds like the issue!

    So these should both go in the final hunk right- something like this?

    Code (CSharp):
    1. fixed4 frag (v2f i) : SV_Target
    2.             {
    3.                 bool isInClip = i.uvShadow.w > 0.0 && i.uvShadow.x > 0 && i.uvShadow.x < 1 && i.uvShadow.y > 0 && i.uvShadow.y < 1;
    4.                 fixed4 texS = tex2Dproj (_ShadowTex, UNITY_PROJ_COORD(i.uvShadow));
    5.                 texS.a = (1.0-texS.a) * isInClip;
    6.                 return texS;
    7.             }
    Have set up a fresh project to test, but no luck :(

    I'm also getting the following warning in the editor -
    Thanks again for help so far!
     
    Last edited: Apr 8, 2015
  4. tomatosource

    tomatosource

    Joined:
    Dec 12, 2014
    Posts:
    12
    GOT IT! Just needed to discard it if outside bounds like so -
    Code (CSharp):
    1. fixed4 frag (v2f i) : SV_Target
    2.             {
    3.                 bool isInClip = i.uvShadow.w > 0.0 && i.uvShadow.x > 0 && i.uvShadow.x < 1 && i.uvShadow.y > 0 && i.uvShadow.y < 1;
    4.                 if(!isInClip)
    5.                     discard;
    6.                 fixed4 texS = tex2Dproj (_ShadowTex, UNITY_PROJ_COORD(i.uvShadow));
    7.                 return texS;
    8.             }
    Thanks heaps cowtrix!!
     
  5. cowtrix

    cowtrix

    Joined:
    Oct 23, 2012
    Posts:
    322
    Ah yep, didn't see that you were using an opaque shader, so setting the alpha wouldn't work. In future, if you're using a transparent shader, the `texS.a=(1.0-texS.a)* isInClip;` line should work. Glad I could help!
     
  6. tomatosource

    tomatosource

    Joined:
    Dec 12, 2014
    Posts:
    12
    Ah that makes sense, will remember for next time - shaders are tricky.
    Thanks again!
     
  7. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    I'm not sure how things are these days, but traditionally GPU's aren't made to handle booleans very well. I would just use clip instead of discard. So instead of:
    Code (csharp):
    1.  
    2. bool isInClip = i.uvShadow.w > 0.0 && i.uvShadow.x > 0 && i.uvShadow.x < 1 && i.uvShadow.y > 0 && i.uvShadow.y < 1;
    3. if (!isInClip) discard;
    4.  
    Use:
    Code (csharp):
    1.  
    2. clip(i.uvShadow.xyw);
    3. clip(1.0 - i.uvShadow.xy);
    4.  
    Makes for cleaner code too in my opinion.
     
  8. tomatosource

    tomatosource

    Joined:
    Dec 12, 2014
    Posts:
    12
    Works like a charm, thanks jvo3dc!
     
  9. tomatosource

    tomatosource

    Joined:
    Dec 12, 2014
    Posts:
    12
    One last question- the projectors seem to be covering shadows, any quick fix for this?
     
  10. tomatosource

    tomatosource

    Joined:
    Dec 12, 2014
    Posts:
    12
    Ended up going back and tweaking default shader to fix shadows, looks like this now:

    Code (CSharp):
    1. Shader "Projector/Texture" {
    2.   Properties {
    3.     _ShadowTex ("MainTexture", 2D) = "gray" {}
    4.   }
    5.   Subshader {
    6.     Tags {"Queue"="Transparent"}
    7.     Pass {
    8.       ZWrite Off
    9.       ColorMask RGB
    10.       Blend DstColor Zero
    11.       Offset -1, -1
    12.  
    13.       CGPROGRAM
    14.       #pragma vertex vert
    15.       #pragma fragment frag
    16.       #pragma multi_compile_fog
    17.       #include "UnityCG.cginc"
    18.      
    19.       struct v2f {
    20.         float4 uvTexture : TEXCOORD0;
    21.         UNITY_FOG_COORDS(2)
    22.         float4 pos : SV_POSITION;
    23.       };
    24.      
    25.       float4x4 _Projector;
    26.       float4x4 _ProjectorClip;
    27.      
    28.       v2f vert (float4 vertex : POSITION)
    29.       {
    30.         v2f o;
    31.         o.pos = mul (UNITY_MATRIX_MVP, vertex);
    32.         o.uvTexture = mul (_Projector, vertex);
    33.         UNITY_TRANSFER_FOG(o,o.pos);
    34.         return o;
    35.       }
    36.      
    37.       sampler2D _ShadowTex;
    38.      
    39.       fixed4 frag (v2f i) : SV_Target
    40.       {
    41.         clip(i.uvTexture.xyw);
    42.         clip(1.0 - i.uvTexture.xy);
    43.         fixed4 texS = tex2Dproj (_ShadowTex, UNITY_PROJ_COORD(i.uvTexture));
    44.         texS.a = 1.0-texS.a;
    45.         return texS;
    46.       }
    47.       ENDCG
    48.     }
    49.   }
    50. }
    51.  
    Hopefully this helps anyone looking to do the same one day.
     
  11. Hikinghack

    Hikinghack

    Joined:
    Jan 9, 2019
    Posts:
    5
    Ah! i am that person hunting for exactly this one day!
     
  12. Hikinghack

    Hikinghack

    Joined:
    Jan 9, 2019
    Posts:
    5
    Looks like there needs to be some changes though for this to work with unity after 4 years has passed
     
  13. Hikinghack

    Hikinghack

    Joined:
    Jan 9, 2019
    Posts:
    5
    Hmm so for some reason the lower side of the cutoff works great!
    But the upper side (uv's greater than 1) turn my projector into a variable sized window that changes shape as I move it (and also only takes up 1/3 of the picture)
     
  14. Hikinghack

    Hikinghack

    Joined:
    Jan 9, 2019
    Posts:
    5
    Here is an example
    The right side of the image changes when I try to use clip(1.0-i.uvShadow.xy)
     
  15. mattdevv

    mattdevv

    Joined:
    Dec 28, 2019
    Posts:
    12
    The above solution works if you clip after the coordinates are projected.
    Code (CSharp):
    1. fixed4 frag (v2f i) : SV_Target
    2.             {
    3.                 clip(i.uvShadow.xy);
    4.  
    5.                 fixed4 texS = tex2Dproj (_ShadowTex, UNITY_PROJ_COORD(i.uvShadow));
    6.  
    7.                 clip(1.0 - i.uvShadow.xy);
    8.  
    9.                 texS.rgb *= _Color.rgb;
    10.                 texS.a = 1.0-texS.a;
    11.    
    12.                 fixed4 texF = tex2Dproj (_FalloffTex, UNITY_PROJ_COORD(i.uvFalloff));
    13.                 fixed4 res = texS * texF.a;
    14.  
    15.                 UNITY_APPLY_FOG_COLOR(i.fogCoord, res, fixed4(0,0,0,0));
    16.  
    17.                
    18.                 return res;
    19.             }
     
    Hikinghack likes this.