Search Unity

Simplest possible fragment shader that has shadows

Discussion in 'Shaders' started by lifeformed, Aug 1, 2012.

  1. lifeformed

    lifeformed

    Joined:
    Aug 1, 2012
    Posts:
    51
    I'm trying to write a custom shader that has shadows. I've tried everything and I can't seem to get it to work.
    Here is the code that I have. I just want the simplest possible non-surface shader that accepts shadows. I know I can just use a surface shader but I need to do some more advanced operations after I get shadows working.

    Code (csharp):
    1. Shader "Custom/Test"
    2. {
    3.     SubShader
    4.     {
    5.         Pass
    6.         {
    7.             CGPROGRAM
    8.             #pragma vertex vert
    9.             #pragma fragment frag
    10.             #pragma multi_compile_fwdadd
    11.             #include "UnityCG.cginc"
    12.             #include "AutoLight.cginc"
    13.    
    14.             struct v2f
    15.             {
    16.                 float4 pos : SV_POSITION;
    17.                 LIGHTING_COORDS(0,1)
    18.             };
    19.  
    20.             v2f vert (appdata_full v)
    21.             {
    22.                 v2f o;
    23.                 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    24.                 TRANSFER_VERTEX_TO_FRAGMENT(o);
    25.                 return o;
    26.             }
    27.  
    28.             float4 frag (v2f i) : COLOR
    29.             {
    30.                 return LIGHT_ATTENUATION(i);
    31.             }
    32.             ENDCG
    33.         }
    34.     }
    35.     FallBack "Diffuse"
    36. }
    37.  
    This is what it looks like in a simple room filled with pillars:

    Note that there are two issues:
    1. There are no shadows between the pillars.
    2. The light is in the top right area of the room, even though the light is on the left. The lighting stays the same even if I move the light around. This is probably a separate issue from the shadows though.

    Thanks for your help.
     
  2. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    #pragma multi_compile_forwardadd

    should be

    #pragma multi_compile_fwdbase
     
  3. lifeformed

    lifeformed

    Joined:
    Aug 1, 2012
    Posts:
    51
    When I do that, it return 1.0 for every value of atten (everything is white).
     
  4. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    Well, by default Unity will give you one shadow in forward rendering mode.

    This gets given to the fwd_base pass and is the shadow cast from the most prominent directional light affecting that object.

    If you're not using directional lights, you won't get shadows. Also the attenuation for a directional light will always be 1 if that light has no shadows (directional lights never attenuate).

    Try setting the FallBack to "VertexLit" (technically Diffuse should do that anyway, but it's worth cutting out the middle man). VertexLit contains passes that give the shadow caster/reciever information to the lighting system, so it has to be in the fallback for shadows to be calculated at all. You could make your own fallback with just those in... but vertexlit's an easy and known-to-work alternative.
     
  5. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    Oh, damn, I forgot.

    Vertex lit REQUIRES that you have a property of _Color in your properties (just copy/paste it from the other Unity shaders if you like). Otherwise you don't get no shadows at all :p

    If you're using cutout/transparent vertex lit it will also require _MainTex in there so that it can derive the alpha from that with which to cast shadows.
     
  6. lifeformed

    lifeformed

    Joined:
    Aug 1, 2012
    Posts:
    51
    Oh oops. I'm using deferred rendering; I guess I should have mentioned that.
    I put in "#pragma multi_compile_fwdadd" because without it, it would comment out my shader and complain about it being in Unity 2.x style and needing to change it to a surface shader. Is there a deferred version of that pragma command that would let me use shadows?
     
  7. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    I believe that even if you're running deferred, it'll just render that shader in forward rendering if it doesn't have a deferred path.

    I'm not sure what the deferred pipeline is for custom shaders (or if it's even accessible that way without using surface shaders) sorry :/

    You could try making a really simple deferred surface shader and open up the compiled version to have a look.
     
  8. lifeformed

    lifeformed

    Joined:
    Aug 1, 2012
    Posts:
    51
    Hmmm... well, maybe I could render my shader with the advanced stuff without shadows, and then render another pass with just shadows, and blend them together?

    Do you think that's a viable option? If so, I'm having trouble combining them: how do I get a surface shader into a Pass? I need my second Pass to blend with my first one, but I can't seem to get shadows working in anything other than a surface shader.
     
  9. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    The deferred pipeline's pretty locked down... I'm not sure how easy it is to work around it (I've not tried before).

    It's probably possible to render it once and get just the lighting out and then render again with a replacement shader or something to get the albedo.

    What is it you're trying to do, exactly? There may be an easier way.
     
  10. lifeformed

    lifeformed

    Joined:
    Aug 1, 2012
    Posts:
    51
    Dang. Basically, I'm just trying to add shadows to my scene over my own shader.

    Here's what I have now - it's a shader that lights each tile equally. (By the way, what's the term for a non-surface shader? Do I just call them fragment shaders? (That's what this is))



    I'd like to apply some simple shadows on top of that. If I replace that shader with just a super-simple surface shader (just setting albedo to 1), I get this:


    I'd like to blend those two images together to create something like this:

    (note that this doesn't actually exist in game, I just blended the images together in Photoshop to make this one)
    That's the end result I'm trying to achieve (well, except for the little dark bump edges on the tiles, I just want to blend the shadows. But I guess that's a different issue). Is there a way to achieve this blending in Unity?
     
    Gekigengar likes this.
  11. lifeformed

    lifeformed

    Joined:
    Aug 1, 2012
    Posts:
    51
  12. lifeformed

    lifeformed

    Joined:
    Aug 1, 2012
    Posts:
    51
    Sorry, have to bump this thread.

    I'm still on this issue. I have a fragment shader that I need to get some hard shadows from point lights in. It sounds like a simple task but I've spent weeks unsuccessfully trying to get it to work. Is it possible at all to get a fragment shader with shadows? Or at least fake it, through multiple passes and blending?
     
  13. Mikkel-Gjoel990

    Mikkel-Gjoel990

    Joined:
    Feb 18, 2013
    Posts:
    3
    The trick is to use #pragma multi_compile_fwdadd_fullshadows (thanks aras!). This gives you all combinations of spot/point-keywords with shadows as well as without. The functional code then becomes (tested unity4.0):

    Code (csharp):
    1. Shader "Custom/Test Shadowed" {
    2.     Properties {
    3.         _Color ("Main Color", Color) = (1,1,1,1) //note: required but not used
    4.     }
    5.     SubShader {
    6.         Tags { "RenderType"="Opaque" "Queue"="Geometry" }
    7.         Pass {
    8.             Tags { "LightMode" = "ForwardAdd" }
    9.             Blend One One
    10.             Fog { Color(0,0,0,0) }
    11.  
    12.             CGPROGRAM
    13.             #pragma vertex vert
    14.             #pragma fragment frag
    15.             #pragma multi_compile_fwdadd_fullshadows
    16.             #include "UnityCG.cginc"
    17.             #include "AutoLight.cginc"
    18.  
    19.             struct v2f {
    20.                 float4 pos : SV_POSITION;
    21.                 LIGHTING_COORDS(0,1)
    22.             };
    23.  
    24.             v2f vert (appdata_full v) {
    25.                 v2f o;
    26.                 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    27.                 TRANSFER_VERTEX_TO_FRAGMENT(o);
    28.                 return o;
    29.             }
    30.  
    31.             float4 frag (v2f i) : COLOR {
    32.                 return LIGHT_ATTENUATION(i);
    33.             }
    34.             ENDCG
    35.         } //Pass
    36.     } //SubShader
    37.     FallBack "Diffuse" //note: for passes: ForwardBase, ShadowCaster, ShadowCollector
    38. }
     
  14. ZoomDomain

    ZoomDomain

    Joined:
    Aug 30, 2012
    Posts:
    150
    Damn! holy smokes! The above code doesn't work at all! I just transcribed all my shader into it and guess what- if you comment out the "Fallback diffuse" line, it doesn't display anything at all. fortunately it is also amusing, as this was unexpected.
     
    Last edited: Sep 6, 2013
  15. monark

    monark

    Joined:
    May 2, 2008
    Posts:
    1,598
    Trying to do this in Unity 5 with no luck.
    Using forward rendering, 1 directional light casting hard shadows and this test code
    I tried the above code directly unmodified but then the object completely vanishes.
    I saw on another thread that SHADOW_COORDS and TRANSFER_SHADOW is now the way to go so I knocked up the example below, but no joy.

    Code (CSharp):
    1. Shader "Custom/TestShadowed" {
    2.     Properties{
    3.         _Color("Main Color", Color) = (1,1,1,1) //note: required but not used
    4.     }
    5.     SubShader{
    6.         Tags{ "RenderType" = "Opaque" "Queue" = "Geometry" }
    7.         Pass{
    8.             Tags{ "LightMode" = "ForwardBase" }
    9.             Blend One One
    10.             Fog{ Color(0,0,0,0) }
    11.  
    12.             CGPROGRAM
    13.             #pragma vertex vert
    14.             #pragma fragment frag
    15.             #pragma fullforwardshadows
    16.             #pragma noforwardadd
    17.             #pragma multi_compile_fwdbase
    18.             #include "UnityCG.cginc"
    19.             #include "AutoLight.cginc"
    20.  
    21.             struct v2f {
    22.                 float4 pos : SV_POSITION;
    23.                 SHADOW_COORDS(0)
    24.             };
    25.  
    26.             v2f vert(appdata_full v) {
    27.                 v2f o;
    28.                 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    29.                 TRANSFER_SHADOW(o);
    30.                 return o;
    31.             }
    32.  
    33.             float4 frag(v2f i) : COLOR{
    34.                 return SHADOW_ATTENUATION(i);
    35.             }
    36.             ENDCG
    37.         } //Pass
    38.     } //SubShader
    39.         FallBack "Diffuse" //note: for passes: ForwardBase, ShadowCaster, ShadowCollector
    40. }
    41.  
    All I get is white, no shadow receiving at all. It cast shadows but won't receive them.

    Any ideas what is the way to do this in Unity 5.x
     
  16. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
  17. monark

    monark

    Joined:
    May 2, 2008
    Posts:
    1,598
    Yeah that seems to match what I have already. I noticed when I just put this shader into it's own file it does in fact work as expected so there's something else going on. The technique appears to be correct.
     
  18. FortisVenaliter

    FortisVenaliter

    Joined:
    Mar 23, 2013
    Posts:
    48
    For anyone else who comes upon this thread at a later date, I figured it out.

    @monark almost had it right. That will cause it to receive shadows *but not to cast them*. To also cast shadows, after the first pass, add the following line:

    UsePass "Legacy Shaders/VertexLit/SHADOWCASTER"

    That will allow it to both cast *and* receive shadows.