Receive shadows by sprite in 3D world

Hi, Community!
I need some advices to writing a shader or get other solution for the implementation of the intended.
So, I have a sprite that forms a character in different layers.

2d character is in 3d world, and it is necessary that he casts a shadow, reacts to the lighting and receive shadows. If the first 2 terms can be realised by a standard shader, but receive shadows can’t.
I searched quite a lot, but I did not find a correct solution. Solution from this post instead of receiving shadows only shows the shadow behind the object but not on the object.


The most suitable was this solution. But in this case, the sprite “turns” into 3d object and the order of rendering depends on the distance to the camera, but not the layer order. Because of this, it is impossible to animate the character.

However, this method is compatible with various effects such as the depth of field and the similar. Also, I can output sprites on top of all objects with “Ztest Off” and then the order of the layers will be restored.

But there are objects that should overlap the character on camera screen.Therefore this option does not fit.

In general, the option with the last shader is only one I could find where real shadows are realized. However, before using it, I need to make it suitable for my project. Namely, receiving shadows and keeping the order of rendering, depending the layer of the sprite.
It’s worth noting that I’m beginner in shaders. Does anyone know how to reach the needed result? Maybe I’m missing the kind of default implementation method?
Sorry for my English. And thank you in advance!

ONS49 Hey, firstly I have few questions.

  • You mentioned that your world is 3d, so as I understand u don’t use sprites for them?
  • Also the lights in the background is it backed/textures or actual unity lights? I’m guessing you want character shadow to blend with background shadows.
  • Why you need “sprite ordering” instead of “z order” so badly?

Hello, LukasCh. The z-axis ordering is not suitable for me, since the hero consists of parts such as the head, the brush, the shoes, the forearm and so on. All these parts should be in the same plane in relation to the camera. And z ordering is bad, because it causes overlap.


Regarding game world, there are still 3D objects, a perspective camera and single sprites. With single sprites, when using the last shader, there is no problem of overlaping, and therefore sorting by z is suitable for them.
Light in the scene is actual unity light. I’ll describe it in more detail: There is a directional light source, a wall with window hole, and an inner hall.

As you can see, the light passes through the window aperture and does not pass through the outside wall, thereby leaving a shadow on the inner walls. Then I put a sprites(character) with a standard shader(Sprites-diffuse) into the scene, the sprite casts the shadows as it should, but it does not illuminate properly.

Namely, it is illuminated as if there are no objects in front of them. The sprites does not affected by shadows(does not recive shadows). And this happens with all objects and with any types of light.
The second shader from the post above gives the desired result, exactly sprites with it can be darkened by shadows from other objects. However, as I noted, sorting by layers stop work. Using a “crutch” in the form of a small displacement of parts along the z-axis to simulate layers is naturally not suitable, because displacement must be too large to avoid overlapping in animations. Option with the replacement classic animation to frame-by-frame animation is inconvenient.
And my purpose is to achieve that the sprites were under the influence of shadows from other objects and are sorted by layers(so that there were no overlaps)
I hope now the question is clear:)

ONS49, I made small project that sprite captures all effects that u need: Casting shadow/Receiving shadow/Sprite/Reacting to lights. Also I suggest you to avoid using standard shaders in 2D games as it only brings unnecessary cost. I hope this helps.

Also next time use Frame Debugger, there you can see rendering orders, so its pretty easy to deduct if something is not going how u expect.

3208774–245629–TestSprite.zip (24.3 KB)

1 Like

LukasCh, thanks for the answer. Separate thanks for the advice with Frame Debugger and standart shaders in 2D.
Indeed in your project the sprite satisfy my needs for lighting and shadows. However, it does not satisfy the conditions for layer sorting. On your scene I cloned middle sprite, than set clone as child from current, and little displaced it in XY-plane. And if you rotate the parent sprite, you get this picture.


A sprite with a higher layer value does not overlap another with a smaller value. The preservation of sorting by layers is important for the reasons described in the above discussion.
Is it possible at all to keep the affecting of shadows and save sorting by layers together?

In the end, I was able to write a small shader that does everything I need.
Here it is
Shader

Shader “Sprites/LayerShadowSprite” {
Properties {
[PreRendererData] _MainTex (“Sprite Texture”, 2D) = “white” {}
_Color (“Color”, Color) = (1,1,1,1)
_Cutoff (“Shadow alpha cutoff”, Range(0,1)) = 0.5
}
SubShader {
Tags {
“Queue”=“AlphaTest”
“IgnoreProjector”=“True”
“RenderType”=“TransparentCutout”
“PreviewType”=“Plane”
“CanUseSpriteAtlas”=“True”
}
LOD 200
Cull off
Lighting On
Zwrite Off

CGPROGRAM
#pragma surface surf Lambert addshadow fullforwardshadows
//#pragma surface surf Lambert addshadow alphatest:_CutOff
#pragma target 3.0

sampler2D _MainTex;
fixed4 _Color;
fixed _Cutoff;

struct Input {
float2 uv_MainTex;
};

void surf (Input IN, inout SurfaceOutput o) {

fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Alpha = c.a;
clip(o.Alpha - _Cutoff);
}
ENDCG
}
FallBack “Legacy Shaders/Transparent/Cutout/VertexLit”
}

It works with lighting and shadows and preserves sorting by layers. However, there are some defects:

  1. Sky overlaps the sprite if camera have skybox

    2)The borders of the sprite became little more inaccurate than in the usual case
  2. This is no longer a question about the shader, but when I using Ambient Occlusion effect, then the sprite works with the effect only when the parent’s gameobject is turned to scale > 0. Once the parent object is set scale to < 0 (flip), the sprite is now perceived by the effect on the reverse side. This leads to incorrect shading over the sprite. Although the face of the sprite still looks at the camera. And the Depth of Field effect works in both directions. ¯_(ツ)_/¯

Can someone help me to finish the shader or give advice, how to avoid these defects please?

1 Like

ONS49

  1. It happens because your shader doesn’t write z and skybox is drawn as the last object (In the end it replaces as no z written there). This way we save performance as full screen draw is expensive and very rarely in games u see sky in all screen. I recommend not using skybox.
  2. Not really sure I understood this correctly. But I’m guessing ure talking about edge aliasing, maybe 2d sprites has some additional options for it. Maybe Unity - Manual: ShaderLab command: Blend ‘AlphaToMask On’ could to the magic.
  3. Not sure about this one, but maybe its worth to add x-flip to the shader as option (Just sample texture with 1-x coordinate, it might need changes to shadowcaster pass too)? This way you would avoid negative scaling voodoo magic.

Any way I strongly recommend you dropping 2D sprites and just use 3D rendering here, because seems ure project depends on too many 3d features skybox/depth based post effects/lighting/shadows. Of course u will lose layer sorting and will have to depend on z order, but in result you will avoid lots of this corner issue you having and will have other ones in future.