Search Unity

Sprite receive shadow

Discussion in '2D' started by trialforce, Sep 28, 2015.

Thread Status:
Not open for further replies.
  1. trialforce

    trialforce

    Joined:
    Apr 15, 2012
    Posts:
    22
    Hi People!

    After a lot of research I finally manage to make sprites cast shadows, but I can't make it receive.

    Like you can see in the image, the box can receive and cast shadow, but the sprite only cast.

    I have castShadow and Receive shadow checked.

    Perhaps is my Shader?

    Can someone help me?
     

    Attached Files:

  2. Kuan

    Kuan

    Unity Technologies

    Joined:
    Jul 2, 2014
    Posts:
    87
    Hi! May I know what shader are you using?
     
  3. trialforce

    trialforce

    Joined:
    Apr 15, 2012
    Posts:
    22
    Hi!

    I get the shader here in forum, it's the default sprite with support to cast shadow.

    I try legacy/transparent/cutout with modifications, it gave good results, but still not good enough.

    Some suggestions?
     
  4. trialforce

    trialforce

    Joined:
    Apr 15, 2012
    Posts:
    22
    Hi!

    I managed to get.

    This shader, make 2d sprites cast and receive shadows, BUT you can't have negative order in layer (sortingOrder).

    And you still have to active in renderer.

    Code (CSharp):
    1.  
    2. GetComponent<Renderer>().shadowCastingMode =  UnityEngine.Rendering.ShadowCastingMode.On;
    3. GetComponent<Renderer>().receiveShadows = true;
    4.  
    It's real unity3d shadow.

    I hope it help others.

    Code (CSharp):
    1. Shader "Sprites/Beat/Diffuse-Shadow"
    2. {
    3.     Properties
    4.     {
    5.         [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
    6.         _Color ("Tint", Color) = (1,1,1,1)
    7.         _Cutoff ("Shadow alpha cutoff", Range(0,1)) = 0.5
    8.     }
    9.  
    10.     SubShader
    11.     {
    12.         Tags
    13.         {
    14.             "Queue"="AlphaTest"
    15.             "IgnoreProjector"="True"
    16.             "RenderType"="TransparentCutout"
    17.             "PreviewType"="Plane"
    18.             "CanUseSpriteAtlas"="True"
    19.         }
    20.  
    21.         LOD 200
    22.         Cull Off
    23.         Lighting On
    24.         ZWrite Off
    25.  
    26.         CGPROGRAM
    27.         #pragma surface surf Lambert addshadow alphatest:_Cutoff
    28.  
    29.         sampler2D _MainTex;
    30.         fixed4 _Color;
    31.  
    32.         struct Input
    33.         {
    34.             float2 uv_MainTex;
    35.         };
    36.  
    37.         void surf (Input IN, inout SurfaceOutput o)
    38.         {
    39.             fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    40.             o.Albedo = c.rgb;
    41.             o.Alpha = c.a;
    42.         }
    43.         ENDCG
    44.     }
    45.  
    46.     Fallback "Legacy Shaders/Transparent/Cutout/VertexLit"
    47. }
    48.  
     
  5. AwDogsGo2Heaven

    AwDogsGo2Heaven

    Joined:
    Jan 17, 2014
    Posts:
    102
    Ive done the above but still dont see shadows? any ideas? :(

    edit: All i have is two sprites, a directional light, and a terrain. I see no shadow casted by the sprite on to the terrain, but the terrain itself has shading.

    edit: turns out my shadow distance in the settings was too low!
     
    Last edited: Oct 2, 2015
  6. AwDogsGo2Heaven

    AwDogsGo2Heaven

    Joined:
    Jan 17, 2014
    Posts:
    102
    Well I got it working in editor, but doesn't work on iOS for some reason. Trial you ever try this in mobile
    Edit: nvm, turns out to be the quality settings again, though I didn't notice the green check marks.
     
    Last edited: Oct 2, 2015
  7. trialforce

    trialforce

    Joined:
    Apr 15, 2012
    Posts:
    22
    Did it work? I'm glad I can help.
     
  8. AwDogsGo2Heaven

    AwDogsGo2Heaven

    Joined:
    Jan 17, 2014
    Posts:
    102
    It worked Trial, and thanks! Right now I'm trying to get it to cast on an invisible plane though, because my sprites 'face' at an angle (like isometric, but not so much). I need the shadow to basically start from their legs not bottom of the feet (otherwise they look like they are floating). I was looking at this shader, and it works on OSX but doesn't on iOS:

    http://forum.unity3d.com/threads/matte-shadow.14438/#post-2321690
     
  9. Entrack

    Entrack

    Joined:
    Oct 26, 2015
    Posts:
    1
    I won't say it doesn't work, but still despite the fact I've done everything according to instructions, it does not.
    Do you know what's the problem?
    Shadows cast only on 3D objects, but not on 2D sprites.

    I've placed an
    - Object sprite
    - Cube
    - Background sprite

    As you can see (pic related) Object's shadow is only on the cube.
    Every element has it's cast/receiveShadow On (I've turned it on in the Inspector Debug mode). Spotlight has enabled shadows as well.
    I would appreciate any help :)

    P.S. I have no transparency on any of my PNGs, but they are still semitransparent.
     
    Last edited: Oct 27, 2015
  10. trialforce

    trialforce

    Joined:
    Apr 15, 2012
    Posts:
    22
    Hi!

    Sorry, I only see your post now. can you send a zip with your project, I can have a look for you.
     
  11. neroziros

    neroziros

    Joined:
    Aug 25, 2012
    Posts:
    129
    I'm expriencing a similar issue, my sprites are able to cast shadows but not receive them. Could it be a 5.2.3 problem?
     
  12. trialforce

    trialforce

    Joined:
    Apr 15, 2012
    Posts:
    22
    Hi!

    I will make a working example. Please wait a couple days.

    Regards.
     
  13. captnflem

    captnflem

    Joined:
    Nov 5, 2015
    Posts:
    7
    Hello and thanks for sharing!

    Can you explain me a bit where did you write these lines of code you are talking about here:
    Is it on the shader itselft?

    Thanks a lot
     
  14. Yukichu

    Yukichu

    Joined:
    Apr 2, 2013
    Posts:
    420
    Put it in a script attached to the sprite, in the Awake() or Start() callback. You need to enable the shadows via code since it isn't exposed in the editor.

     
    captnflem likes this.
  15. captnflem

    captnflem

    Joined:
    Nov 5, 2015
    Posts:
    7
    Thx I'll try it asap!
     
  16. Kiberia

    Kiberia

    Joined:
    Jan 12, 2016
    Posts:
    2
    Trialforce, have you find any solution yet? Using your shader with Sprite and Directional light make all my sprites simply disappear )
     
    Last edited: Jan 12, 2016
  17. Tom-Atom

    Tom-Atom

    Joined:
    Jun 29, 2014
    Posts:
    153
    Hi, you can set shadow receiver/caster even without script. If you click on small menu icon in almost top-right corner (next to pad lock) and select Debug then you can set both these properties:

    UnityShadows.png
     
    zw3636, Zariellarsen, no00ob and 5 others like this.
  18. anlev

    anlev

    Joined:
    Jun 14, 2015
    Posts:
    9
    so did anyone get the solution of shadow receiving?
     
  19. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    You should use a cutout shader if you want to receive shadows. Because transparent pass cannot due to being rendered after geometry pass for sorting purposes. It's a well known common issue with GPU design.
     
  20. anlev

    anlev

    Joined:
    Jun 14, 2015
    Posts:
    9
    what do u mean under cutout shader?
     
  21. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Any cutout shader receives shadows by default because they're rendered with geometry ?
     
  22. anlev

    anlev

    Joined:
    Jun 14, 2015
    Posts:
    9
    oh, i realised, that i totally noob in shaders :(
    i'll be very grateful if you check my topic here:
    http://forum.unity3d.com/threads/2d-sprite-cant-recieve-shadows.382544/
    how could i modify this shader? make some cutout pass or something?
    searched through the entire unity3d-forum, but nothing worked for me :(((
     
  23. anlev

    anlev

    Joined:
    Jun 14, 2015
    Posts:
    9
    montblanc, shaggun and theANMATOR2b like this.
  24. Stevepunk

    Stevepunk

    Joined:
    Oct 20, 2013
    Posts:
    205
    This is great but how can I add this to an existing shader?

    For example the Spritelamp shader:
    // Shader for Unity integration with SpriteLamp. Currently the 'kitchen sink'
    // shader - contains all the effects from Sprite Lamp's preview window using the default shader.
    // Based on a shader by Steve Karolewics & Indreams Studios. Final version by Finn Morgan
    // Note: Finn is responsible for spelling 'colour' with a U throughout this shader. Find/replace if you must.


    Shader "SpriteLamp/Standard_PerTexel"
    {
    Properties
    {
    _MainTex ("Diffuse Texture", 2D) = "white" {} //Alpha channel is plain old transparency
    _NormalDepth ("Normal Depth", 2D) = "bump" {} //Normal information in the colour channels, depth in the alpha channel.
    _SpecGloss ("Specular Gloss", 2D) = "" {} //Specular colour in the colour channels, and glossiness in the alpha channel.
    _AmbientOcclusion ("Ambient Occlusion", 2D) = "" {} //A greyscale value for precomputed ambient occlusion - not very compact.
    _EmissiveColour ("Emissive colour", 2D) = "" {} //A colour image that is simply added over the final colour. Might eventually have AO packed into its alpha channel.

    _SpecExponent ("Specular Exponent", Range (1.0,50.0)) = 10.0 //Multiplied by the alpha channel of the spec map to get the specular exponent.
    _SpecStrength ("Specular Strength", Range (0.0,5.0)) = 1.0 //Multiplier that affects the brightness of specular highlights
    _AmplifyDepth ("Amplify Depth", Range (0,1.0)) = 0.0 //Affects the 'severity' of the depth map - affects shadows (and shading to a lesser extent).
    _CelShadingLevels ("Cel Shading Levels", Float) = 0 //Set to zero to have no cel shading.
    _TextureRes("Texture Resolution", Vector) = (256, 256, 0, 0) //Leave this to be set via a script.
    _AboveAmbientColour("Upper Ambient Colour", Color) = (0.3, 0.3, 0.3, 0.3) //Ambient light coming from above.
    _BelowAmbientColour("Lower Ambient Colour", Color) = (0.1, 0.1, 0.1, 0.1) //Ambient light coming from below.
    _LightWrap("Wraparound lighting", Range (0,1.0)) = 0.0 //Higher values of this will cause diffuse light to 'wrap around' and light the away-facing pixels a bit.
    _AmbientOcclusionStrength("Ambient Occlusion Strength", Range (0,1.0)) = 0.0 //Determines how strong the effect of the ambient occlusion map is.
    _EmissiveStrength("Emissive strength", Range(0, 1.0)) = 0.0 //Emissive map is multiplied by this.
    _AttenuationMultiplier("Attenuation multiplier", Range(0.1, 5.0)) = 1.0 //Distance is multiplied by this for purposes of calculating attenuation
    _SpotlightHardness("Spotlight hardness", Range(1.0, 10.0)) = 2.0 //Higher number makes the edge of a spotlight harder.
    }

    SubShader
    {
    Tags
    {
    "Queue"="Transparent"
    "IgnoreProjector"="True"
    "RenderType"="Transparent"
    "PreviewType"="Plane"
    "CanUseSpriteAtlas"="True"
    }

    Cull Off
    Lighting Off
    ZWrite Off
    Fog { Mode Off }
    Blend SrcAlpha OneMinusSrcAlpha
    AlphaTest NotEqual 0.0

    Pass
    {
    Tags { "LightMode" = "ForwardBase" }

    CGPROGRAM

    #pragma vertex vert
    #pragma fragment frag
    #pragma target 3.0

    #include "UnityCG.cginc"
    #include "AutoLight.cginc"

    uniform sampler2D _MainTex;
    uniform sampler2D _NormalDepth;
    uniform sampler2D _SpecGloss;
    uniform sampler2D _AmbientOcclusion;
    uniform sampler2D _EmissiveColour;
    uniform float4 _AboveAmbientColour;
    uniform float4 _BelowAmbientColour;
    uniform float _AmbientOcclusionStrength;
    uniform float _EmissiveStrength;
    uniform float _AttenuationMultiplier;
    uniform float4 _LightColor0;
    uniform float _SpecExponent;
    uniform float _AmplifyDepth;
    uniform float _CelShadingLevels;
    uniform float4 _TextureRes;
    uniform float _LightWrap;
    uniform float _SpecStrength;
    uniform float4x4 _LightMatrix0; // transformation
    uniform float _SpotlightHardness;


    struct VertexInput
    {
    float4 vertex : POSITION;
    float4 color : COLOR;
    float4 uv : TEXCOORD0;
    };

    struct VertexOutput
    {
    float4 pos : POSITION;
    float4 color : COLOR;
    float2 uv : TEXCOORD0;
    float4 posWorld : TEXCOORD1;
    float4 posLight : TEXCOORD2;
    };

    VertexOutput vert(VertexInput input)
    {
    VertexOutput output;

    output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
    output.posWorld = mul(_Object2World, input.vertex);

    output.uv = input.uv.xy;
    output.color = input.color;
    output.posLight = mul(_LightMatrix0, output.posWorld);
    return output;
    }

    float4 frag(VertexOutput input) : COLOR
    {
    float4 diffuseColour = tex2D(_MainTex, input.uv);
    float4 normalDepth = tex2D(_NormalDepth, input.uv);
    float ambientOcclusion = tex2D(_AmbientOcclusion, input.uv).r;
    float3 emissiveColour = tex2D(_EmissiveColour, input.uv).rgb;
    float4 specGlossValues = tex2D(_SpecGloss, input.uv);

    float4 ambientResult;

    ambientOcclusion = (ambientOcclusion * _AmbientOcclusionStrength) + (1.0 - _AmbientOcclusionStrength);

    float3 worldNormalDirection = (normalDepth.xyz - 0.5) * 2.0;

    worldNormalDirection = float3(mul(float4(worldNormalDirection, 1.0), _World2Object).xyz);

    float upness = worldNormalDirection.y * 0.5 + 0.5; //'upness' - 1.0 means the normal is facing straight up, 0.5 means horizontal, 0.0 straight down, etc.

    float4 ambientColour = (_BelowAmbientColour * (1.0 - upness) + _AboveAmbientColour * upness) * ambientOcclusion;


    ambientResult = float4(ambientColour * diffuseColour + float4(emissiveColour * _EmissiveStrength, 0.0));

    //We have to calculate illumination here too, because the first light that gets rendered
    //gets folded into the ambient pass apparently.
    //Get the real vector for the normal,
    float3 normalDirection = (normalDepth.xyz - 0.5) * 2.0;
    normalDirection.z *= -1.0;
    normalDirection = normalize(normalDirection);




    float depthColour = normalDepth.a;


    ////PER TEXEL STUFF STARTS////
    //For per-texel lighting, we recreate the world position based on the sprite's UVs...
    float2 positionOffset = input.uv;
    float2 roundedUVs = input.uv;

    //Intervening here to round the UVs to the nearest 1.0/TextureRes to clamp the world position
    //to the nearest pixel...
    roundedUVs *= _TextureRes.xy;
    roundedUVs = floor(roundedUVs) + 0.5;
    roundedUVs /= _TextureRes.xy;


    //This is the per-texel stuff! It's a work in progress - that's why it's commented out right now.

    float2 uvCorrection = input.uv - roundedUVs;

    //Get tangent and bitangent stuff:
    float3 p_dx = ddx(input.posWorld.xyz);
    float3 p_dy = ddy(input.posWorld.xyz);
    //Rate of change of the texture coords
    float2 tc_dx = ddx(input.uv.xy);
    float2 tc_dy = ddy(input.uv.xy);
    //Initial tangent and bitangent

    float3 fragTangent = ( tc_dy.y * p_dx - tc_dx.y * p_dy ) / (length(p_dy) * length(p_dy));
    float3 fragBitangent = ( tc_dy.x * p_dx - tc_dx.x * p_dy ) / (length(p_dx) * length(p_dx));

    fragTangent /= (length(fragTangent) * length(fragTangent));
    fragBitangent /= (length(fragBitangent) * length(fragBitangent));

    //This corrects for nonuniform scales that reverse the handedness of the whole thing.
    float uvCross = clamp((cross(fragTangent, fragBitangent)).z * -10000000.0, -1.0, 1.0);

    float3 uVector = -normalize(fragTangent) * length(fragBitangent) * uvCross;
    float3 vVector = normalize(fragBitangent) * length(fragTangent) * uvCross;

    float3 posWorld = input.posWorld.xyz + uVector * -uvCorrection.x + vVector * -uvCorrection.y;
    ///PER TEXEL STUFF ENDS

    posWorld.z -= depthColour * _AmplifyDepth; //The fragment's Z position is modified based on the depth map value.
    float3 vertexToLightSource;
    float3 lightDirection;
    float attenuation;
    if (0.0 == _WorldSpaceLightPos0.w) // directional light?
    {
    //This handles directional lights
    lightDirection = float3(mul(float4(_WorldSpaceLightPos0.xyz, 1.0), _Object2World).xyz);
    lightDirection = normalize(lightDirection);
    attenuation = 1.0;
    }
    else
    {
    //This code is for point/spot lights. Note that light cookies aren't yet handled for spot lights yet (FIXME)
    float cookieAttenuation = 1.0;
    vertexToLightSource = float3(_WorldSpaceLightPos0.xyz) - posWorld;

    float lightDistance = length(vertexToLightSource);

    if (1.0 != _LightMatrix0[3][3]) //If this is a spotlight, calculate cookie attenuation.
    {
    //This number, 'distance from centre', is the distance this fragment is from the centre line
    //of the spot light. If it is greater than 1.0, this fragment is outside the light cone and shouldn't be
    //illuminated.
    float distanceFromCentre = length (float2(input.posLight.xy) / input.posLight.w) * 2.0;


    //Fairly simplistic implementation of a default spotlight shape. Not total rubbish like the last one was,
    //and doesn't require a texture lookup, but still probably not perfect.
    cookieAttenuation = (1.0 - distanceFromCentre) * _SpotlightHardness * _SpotlightHardness;
    cookieAttenuation = clamp(cookieAttenuation, 0.0, 1.0);
    }

    lightDirection = float3(mul(float4(vertexToLightSource, 1.0), _Object2World).xyz);
    lightDirection = normalize(lightDirection);

    //Linear attenuation at the moment. I'd like to calculate this using Unity's built in attenuation texture
    //lookup method, because it scales nicely with light range, but it seems to only work for spotlights\
    //for some reason.
    attenuation = (1.0 / (1.0 + lightDistance * lightDistance * _AttenuationMultiplier)) - 0.02;
    attenuation = clamp(attenuation, 0.0, 1.0);

    attenuation *= cookieAttenuation;
    }


    float aspectRatio = _TextureRes.x / _TextureRes.y;

    //We calculate shadows here. Magic numbers incoming (FIXME).
    float shadowMult = 1.0;
    float3 moveVec = lightDirection.xyz * 0.006 * float3(1.0, aspectRatio, -1.0);
    float thisHeight = depthColour * _AmplifyDepth;

    float3 tapPos = float3(roundedUVs, thisHeight + 0.1);
    //This loop traces along the light ray and checks if that ray is inside the depth map at each point.
    //If it is, darken that pixel a bit.
    for (int i = 0; i < 8; i++)
    {
    tapPos += moveVec;
    float tapDepth = tex2D(_NormalDepth, tapPos.xy).a * _AmplifyDepth;
    if (tapDepth > tapPos.z)
    {
    shadowMult -= 0.125;
    }
    }
    shadowMult = clamp(shadowMult, 0.0, 1.0);





    // Compute diffuse part of lighting
    float normalDotLight = dot(normalDirection, lightDirection);

    //Slightly awkward maths for light wrap.
    float diffuseLevel = clamp(normalDotLight + _LightWrap, 0.0, _LightWrap + 1.0) / (_LightWrap + 1.0) * attenuation * shadowMult;

    // Compute specular part of lighting
    float specularLevel;
    if (normalDotLight < 0.0)
    {
    // Light is on the wrong side, no specular reflection
    specularLevel = 0.0;
    }
    else
    {
    // For the moment, since this is 2D, we'll say the view vector is always (0, 0, -1).
    //This isn't really true when you're not using a orthographic camera though. FIXME.
    float3 viewDirection = float3(0.0, 0.0, -1.0);
    specularLevel = attenuation * pow(max(0.0, dot(reflect(-lightDirection, normalDirection),
    viewDirection)), _SpecExponent * specGlossValues.a) * 0.4;
    }

    // Add cel-shading if enough levels were specified
    if (_CelShadingLevels >= 2.0)
    {
    diffuseLevel = floor(diffuseLevel * _CelShadingLevels) / (_CelShadingLevels - 0.5);
    specularLevel = floor(specularLevel * _CelShadingLevels) / (_CelShadingLevels - 0.5);
    }

    //The easy bits - assemble the final values based on light and map colours and combine.
    float3 diffuseReflection = diffuseColour.xyz * input.color.xyz * _LightColor0.xyz * diffuseLevel;
    float3 specularReflection = _LightColor0.xyz * input.color.xyz * specularLevel * specGlossValues.rgb * _SpecStrength;

    float4 finalColour = float4(diffuseReflection + specularReflection, diffuseColour.a) + ambientResult;

    return finalColour;


    }

    ENDCG
    }

    Pass
    {
    Tags { "LightMode" = "ForwardAdd" }
    Blend One One // additive blending

    CGPROGRAM

    #pragma vertex vert
    #pragma fragment frag
    #pragma target 3.0

    #include "UnityCG.cginc"

    // User-specified properties
    uniform sampler2D _MainTex;
    uniform sampler2D _NormalDepth;
    uniform sampler2D _SpecGloss;
    uniform float4 _LightColor0;
    uniform float _SpecExponent;
    uniform float _AmplifyDepth;
    uniform float _CelShadingLevels;
    uniform float4 _TextureRes;
    uniform float _LightWrap;
    uniform float _AttenuationMultiplier;
    uniform float _SpecStrength;

    uniform float4x4 _LightMatrix0; // transformation
    uniform float _SpotlightHardness;

    struct VertexInput
    {
    float4 vertex : POSITION;
    float4 color : COLOR;
    float4 uv : TEXCOORD0;
    };

    struct VertexOutput
    {
    float4 pos : POSITION;
    float4 color : COLOR;
    float2 uv : TEXCOORD0;
    float4 posWorld : TEXCOORD1;
    float4 posLight : TEXCOORD2;
    };

    VertexOutput vert(VertexInput input)
    {
    VertexOutput output;

    output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
    output.posWorld = mul(_Object2World, input.vertex);

    output.uv = input.uv.xy;
    output.color = input.color;
    output.posLight = mul(_LightMatrix0, output.posWorld);
    return output;
    }

    float4 frag(VertexOutput input) : COLOR
    {
    //Do texture reads first, because in theory that's a bit quicker...
    float4 diffuseColour = tex2D(_MainTex, input.uv);
    float4 normalDepth = tex2D(_NormalDepth, input.uv);
    float4 specGlossValues = tex2D(_SpecGloss, input.uv);

    //Get the real vector for the normal,
    float3 normalDirection = (normalDepth.xyz - 0.5) * 2.0;
    normalDirection.z *= -1.0;
    normalDirection = normalize(normalDirection);


    float depthColour = normalDepth.a;

    ////PER TEXEL STUFF STARTS////
    //For per-texel lighting, we recreate the world position based on the sprite's UVs...
    float2 positionOffset = input.uv;
    float2 roundedUVs = input.uv;

    //Intervening here to round the UVs to the nearest 1.0/TextureRes to clamp the world position
    //to the nearest pixel...
    roundedUVs *= _TextureRes.xy;
    roundedUVs = floor(roundedUVs) + 0.5;
    roundedUVs /= _TextureRes.xy;


    //This is the per-texel stuff! It's a work in progress - that's why it's commented out right now.

    float2 uvCorrection = input.uv - roundedUVs;

    //Get tangent and bitangent stuff:
    float3 p_dx = ddx(input.posWorld.xyz);
    float3 p_dy = ddy(input.posWorld.xyz);
    //Rate of change of the texture coords
    float2 tc_dx = ddx(input.uv.xy);
    float2 tc_dy = ddy(input.uv.xy);
    //Initial tangent and bitangent

    float3 fragTangent = ( tc_dy.y * p_dx - tc_dx.y * p_dy ) / (length(p_dy) * length(p_dy));
    float3 fragBitangent = ( tc_dy.x * p_dx - tc_dx.x * p_dy ) / (length(p_dx) * length(p_dx));

    fragTangent /= (length(fragTangent) * length(fragTangent));
    fragBitangent /= (length(fragBitangent) * length(fragBitangent));

    //This corrects for nonuniform scales that reverse the handedness of the whole thing.
    float uvCross = clamp((cross(fragTangent, fragBitangent)).z * -10000000.0, -1.0, 1.0);

    float3 uVector = -normalize(fragTangent) * length(fragBitangent) * uvCross;
    float3 vVector = normalize(fragBitangent) * length(fragTangent) * uvCross;



    float3 posWorld = input.posWorld.xyz + uVector * -uvCorrection.x + vVector * -uvCorrection.y;
    ///PER TEXEL STUFF ENDS

    posWorld.z -= depthColour * _AmplifyDepth; //The fragment's Z position is modified based on the depth map value.
    float3 vertexToLightSource;
    float3 lightDirection;
    float attenuation;
    if (0.0 == _WorldSpaceLightPos0.w) // directional light?
    {
    //This handles directional lights
    lightDirection = float3(mul(float4(_WorldSpaceLightPos0.xyz, 1.0), _Object2World).xyz);
    lightDirection = normalize(lightDirection);
    attenuation = 1.0;
    }
    else
    {
    //This code is for point/spot lights. Note that light cookies aren't yet handled for spot lights yet (FIXME)
    float cookieAttenuation = 1.0;
    vertexToLightSource = float3(_WorldSpaceLightPos0.xyz) - posWorld;

    float lightDistance = length(vertexToLightSource);

    if (1.0 != _LightMatrix0[3][3]) //If this is a spotlight, calculate cookie attenuation.
    {
    //This number, 'distance from centre', is the distance this fragment is from the centre line
    //of the spot light. If it is greater than 1.0, this fragment is outside the light cone and shouldn't be
    //illuminated.
    float distanceFromCentre = length (float2(input.posLight.xy) / input.posLight.w) * 2.0;


    //Fairly simplistic implementation of a default spotlight shape. Not total rubbish like the last one was,
    //and doesn't require a texture lookup, but still probably not perfect.
    cookieAttenuation = (1.0 - distanceFromCentre) * _SpotlightHardness * _SpotlightHardness;
    cookieAttenuation = clamp(cookieAttenuation, 0.0, 1.0);
    }

    lightDirection = float3(mul(float4(vertexToLightSource, 1.0), _Object2World).xyz);
    lightDirection = normalize(lightDirection);

    //Linear attenuation at the moment. I'd like to calculate this using Unity's built in attenuation texture
    //lookup method, because it scales nicely with light range, but it seems to only work for spotlights\
    //for some reason.
    attenuation = (1.0 / (1.0 + lightDistance * lightDistance * _AttenuationMultiplier)) - 0.02;
    attenuation = clamp(attenuation, 0.0, 1.0);

    attenuation *= cookieAttenuation;
    }


    float aspectRatio = _TextureRes.x / _TextureRes.y;

    //We calculate shadows here. Magic numbers incoming (FIXME).
    float shadowMult = 1.0;
    float3 moveVec = lightDirection.xyz * 0.006 * float3(1.0, aspectRatio, -1.0);
    float thisHeight = depthColour * _AmplifyDepth;

    float3 tapPos = float3(roundedUVs, thisHeight + 0.1);
    //This loop traces along the light ray and checks if that ray is inside the depth map at each point.
    //If it is, darken that pixel a bit.
    for (int i = 0; i < 8; i++)
    {
    tapPos += moveVec;
    float tapDepth = tex2D(_NormalDepth, tapPos.xy).a * _AmplifyDepth;
    if (tapDepth > tapPos.z)
    {
    shadowMult -= 0.125;
    }
    }
    shadowMult = clamp(shadowMult, 0.0, 1.0);





    // Compute diffuse part of lighting
    float normalDotLight = dot(normalDirection, lightDirection);

    //Slightly awkward maths for light wrap.
    float diffuseLevel = clamp(normalDotLight + _LightWrap, 0.0, _LightWrap + 1.0) / (_LightWrap + 1.0) * attenuation * shadowMult;

    // Compute specular part of lighting
    float specularLevel;
    if (normalDotLight < 0.0)
    {
    // Light is on the wrong side, no specular reflection
    specularLevel = 0.0;
    }
    else
    {
    // For the moment, since this is 2D, we'll say the view vector is always (0, 0, -1).
    //This isn't really true when you're not using a orthographic camera though. FIXME.
    float3 viewDirection = float3(0.0, 0.0, -1.0);
    specularLevel = attenuation * pow(max(0.0, dot(reflect(-lightDirection, normalDirection),
    viewDirection)), _SpecExponent * specGlossValues.a) * 0.4;
    }

    // Add cel-shading if enough levels were specified
    if (_CelShadingLevels >= 2.0)
    {
    diffuseLevel = floor(diffuseLevel * _CelShadingLevels) / (_CelShadingLevels - 0.5);
    specularLevel = floor(specularLevel * _CelShadingLevels) / (_CelShadingLevels - 0.5);
    }

    //The easy bits - assemble the final values based on light and map colours and combine.
    float3 diffuseReflection = diffuseColour.xyz * input.color * _LightColor0.xyz * diffuseLevel;
    float3 specularReflection = _LightColor0.xyz * input.color * specularLevel * specGlossValues.rgb * _SpecStrength;

    float4 finalColour = float4((diffuseReflection + specularReflection) * diffuseColour.a, 0.0);
    return finalColour;

    }

    ENDCG
    }
    }
    // The definition of a fallback shader should be commented out
    // during development:
    Fallback "Transparent/Diffuse"
    }

    http://www.snakehillgames.com/wp-content/uploads/2015/09/SpriteLampUnityShadersOnly_0_5.zip
     
  25. SmilingCatEntertainment

    SmilingCatEntertainment

    Joined:
    Jun 8, 2013
    Posts:
    91
    I wound up with a much simpler solution to this, though in my case I am using billboarded quads instead of Unity's sprite system.

    Since I was already billboarding my quads to the camera, I just adjusted my billboarding code a bit to billboard the normals to the directional light's angle instead of the camera position. This ensures that the lighting is never affected by the angle of the directional light. Then I just use the Standard Shader in Cutout mode and everything is unicorns and rainbows.
     
    theANMATOR2b likes this.
  26. masoudarvishian

    masoudarvishian

    Joined:
    Jan 15, 2015
    Posts:
    9
  27. tcz8

    tcz8

    Joined:
    Aug 20, 2015
    Posts:
    504
    Is this still working for anyone? I just tried it but it does nothing for me. I am using Unity 5.4.1f1
     
  28. trialforce

    trialforce

    Joined:
    Apr 15, 2012
    Posts:
    22
    Hi ! I use 5.4.1f1 and works perfectely, but you can't use negative order in layer.
     
    tcz8 likes this.
  29. tcz8

    tcz8

    Joined:
    Aug 20, 2015
    Posts:
    504
    People, make sure your light sources are set to cast shadows. I feel so stupid HaHA. Seems its set to off by default in 2D.

    EDIT: Well actually, It still doesnt work with the shader in this thread. I just realized the shader I had loaded is this one: https://github.com/anlev/Unity-2D-Sprite-cast-and-receive-shadows

    EDIT #2: OK Nevermind what I said, BOTH shaders work fine now. I didnt realize the Z value was wrong for one of my sprites.

    SO, to resume, make sure your lights are set to cast shadows AND make sure your z value places your sprite above whatever sprite you want to receive shadows ie.: set your background object's z at 0 and your foreground object's z at -0.09.

    Thank you!
     
    Last edited: Feb 2, 2017
    saneangel8 and theANMATOR2b like this.
  30. FernandoByte

    FernandoByte

    Joined:
    Mar 15, 2015
    Posts:
    1
    Thanks! This worked fine. Using Unity 5.5.0f3.
    But I do not know if this has something to do with the RendererExtensions that I have.
     
  31. adsk_marcopaivaf

    adsk_marcopaivaf

    Joined:
    Feb 5, 2015
    Posts:
    3
  32. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Anybody have any idea how to do this with an unlit shader?

    I want my sprites to cast shadows on the background, but I don't want the sprites themselves affected by lighting — they should just blit their colors exactly as the artists designed them.

    EDIT: Never mind, I seem to have figured it out:

    Code (CSharp):
    1. Shader "Sprites/Custom/Unlit-Shadow"
    2. {
    3.     Properties
    4.     {
    5.         [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
    6.         _Color ("Tint", Color) = (1,1,1,1)
    7.         _Cutoff ("Shadow alpha cutoff", Range(0,1)) = 0.5
    8.     }
    9.     SubShader
    10.     {
    11.         Tags
    12.         {
    13.             "Queue"="AlphaTest"
    14.             "IgnoreProjector"="True"
    15.             "RenderType"="TransparentCutout"
    16.             "PreviewType"="Plane"
    17.             "CanUseSpriteAtlas"="True"
    18.         }
    19.         LOD 200
    20.         Cull Off
    21.         Lighting On
    22.         ZWrite Off
    23.         CGPROGRAM
    24.         #pragma surface surf NoLighting addshadow alphatest:_Cutoff
    25.         sampler2D _MainTex;
    26.         fixed4 _Color;
    27.         struct Input
    28.         {
    29.             float2 uv_MainTex;
    30.         };
    31.         fixed4 LightingNoLighting(SurfaceOutput s, fixed3 lightDir, fixed atten)
    32.         {
    33.              fixed4 c;
    34.              c.rgb = s.Albedo;
    35.              c.a = s.Alpha;
    36.              return c;
    37.         }
    38.        
    39.         void surf (Input IN, inout SurfaceOutput o)
    40.         {
    41.             fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    42.             o.Albedo = c.rgb;
    43.             o.Alpha = c.a;
    44.         }
    45.         ENDCG
    46.     }
    47.     Fallback "Legacy Shaders/Transparent/Cutout/VertexUnlit"
    48. }
     
    Gastaldello likes this.
  33. Kaldrin

    Kaldrin

    Joined:
    Jul 10, 2018
    Posts:
    46
    Yes thanks man this solution works fine !
    It just doesn't let me edit the color of a sprite, I have to edit the color of the material, but I'll try to look more into shaders to find a solution and I can bypass this problem anyway :D
     
  34. ouzari

    ouzari

    Joined:
    Jan 31, 2016
    Posts:
    10
    I tried it and it worked, thank you very mush.
     
  35. IndieMarc

    IndieMarc

    Joined:
    Jan 16, 2017
    Posts:
    183
    trialforce your shader don,t work anymore maybe only in the new versions of unity. I had to add this:

    fullforwardshadows

    to the surf Lambert line
     
  36. montblanc

    montblanc

    Joined:
    Feb 13, 2015
    Posts:
    2
    Thank you @anlev, this worked for me, whereas the OP's solution did not. Perhaps it has to do with Unity version?
    Sprites can now cast shadows on both 3D objects and other sprites.
    (I'm on Unity 2019.1.1.f1 macOS)
     
  37. CoCoNutti

    CoCoNutti

    Joined:
    Nov 30, 2009
    Posts:
    513
    I haven't been able to get this to work. Not sure where I'm going wrong - have the github shader on my material, directional light. One object set at 5 on Z, others set at 4, 3 and 2. Direcitonal light cast shadows. Went into debug on each of the 4 sprites and set their shadows on etc..... Still nothing.

    What am I missing here?
     
  38. UDN_068de4e6-4e09-4127-b944-8417609eb2e7

    UDN_068de4e6-4e09-4127-b944-8417609eb2e7

    Joined:
    May 26, 2017
    Posts:
    1
     
  39. TheSwanCollective

    TheSwanCollective

    Joined:
    May 6, 2016
    Posts:
    26
    Nice! The shader works flawlessly in Unity 2019.3!
    Set the Render Queue of the Sprite Material to "Transparent", if you have weird display issues.
     
  40. ShervinM

    ShervinM

    Joined:
    Sep 16, 2017
    Posts:
    67
    While I've been able to get the shader @anlev kindly posted to work, the only problem now is that sorting layers aren't behaving as expected. Im not even using negative sorting orders.
     
  41. knarteyt

    knarteyt

    Joined:
    Apr 19, 2021
    Posts:
    3
    ur a life saver
     
  42. unity_3Ez9FwF7q_YXJg

    unity_3Ez9FwF7q_YXJg

    Joined:
    May 4, 2020
    Posts:
    4
  43. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,500
    Please use the Like button to show yoru appreciation rather than necroing threads.

    Thanks.
     
Thread Status:
Not open for further replies.