Projecting shadow locally per mesh, how to do it ?

Hello all,

I’m making a mobile game a bit like Mario Galaxy where the characters are going around planets which are all are spherical.

I’m running into the issue that Unity’s shadow are too realistic (since the directional light is global), where as I need shadows that are utilitarian are local to each character - to help the player know where the enemies are and where you land (same as in Mario Galaxy).

The problem:

What I would like to achieve is better explained in the picture attached :

I would like to have the shadow projected always like in 1# or #4 regardless of where the character is around the planet. In other words, I would like the shadow to be always at the angle of the where the character’s gravity is like #1 and #4.

In #1 using directional light you can see the shadow looks good, but as we move the character (#2 and #3) the shadow get displaced and it doesn’t look good and is not helpful for gameplay purposes.

Solutions ? :

What would be the most optimized way or some ways to achieve that ? There are many characters on screen and the project is for Mobile. Do I need to write my own lighting system ? Is there a way I can make it work using Unity’s ? Is there a way to do it in a shader ?

I have gone through some trials and tests but I don’t want to use : render to texture, projectors or blob shadows.

Thank you for any input!

Alf

I am actually working on something similar. My solution is to use Render Textures (requires Unity Pro) assigned to Projectors, basically, faking a shadow. I know you don’t want to use them but here is a solution for it in case you need it down the line.

  • Character receiving but not casting shadows
  • Orthographic camera above character rendering only the character (use layers) to a runtime render texture (parent the camera to the character to keep it oriented properly)
  • Orthographic projector at the character’s feet, attached to the character, to project captured image
  • Modify the projector shader to only project the solid color (with render-texture’s alpha) that you want projected

My shader for that so far is here but I need to figure out how to blur the alpha a little to fake soft shadows.

1868359--120027--Example.png

Shader "Custom/RTSProjector"
{
  Properties
  {
     _Color ("Main Color", Color) = (1,1,1,1)  
     _ShadowTex ("Cookie", 2D) = "" { TexGen ObjectLinear }
     _FalloffTex ("FallOff", 2D) = "white" { TexGen ObjectLinear }
  }
  Subshader {
     Tags { "RenderType"="Transparent-1" }
     Pass {
        ZWrite Off
        AlphaTest Greater 0
        Offset -1, -1
        ColorMask RGB
        Blend DstColor Zero
        Color [_Color]
        SetTexture [_ShadowTex] {
            constantColor [_Color]
            combine texture alpha * constant
            Matrix [_Projector]
        }
        SetTexture [_FalloffTex] {
           constantColor (0,0,0,0)
           combine previous lerp (texture) constant
           Matrix [_ProjectorClip]
        }
     }
  }
}

Thanks for the effort. It is appreciated but this solution is already available on the unitywiki : Projected shadows for the characters! - Unity Engine - Unity Discussions

The problem is that if you have 10 characters, the mesh receiving shadow will render 10 times (projector kills performance). On top of that there will be too many render to texture which will be too slow for mobile.

I’m looking for an alternative. Is there any way to do it in a more conventional way ? Maybe its possible to do in a shader ?

Any help is appreciated!!!

Why not use unity’s dir shadows as a child of the player, pointing down?

The reason this particular setup might work is because the character’s orientation and relevancy might mean that most of the time the shadows make sense.

I didnt test it, but i think if you move the shadow casting directional light around the sphere with your character(S) in every OnWillRenderObject(), it should give you the result in #1
But i dont know if WillrenderObject is called while rendering the depthmap or not.

Thanks for the reply Hippo. The thing is that in my project, you often get to see the entirety of the sphere from afar (3d person view like in the picture) so any character that is further away from yours has its shadow deformed…

Thanks Aubergine. I assume you mean “rotate” rather than “move” since moving a directional light does nothing and only its rotation is considered.

As I mentioned to Hippo above the problem is that we get to see the whole sphere from afar (the planets are small) so the problem gets visible very quickly.

Could you elaborate more on how OnWillRenderObject() can help fix the issue ?

Thank you both for the effort, it is appreciated. Hoping there is a solution for this issue…

Well you can always use Graphics.DrawMesh and pass the actual reference of the mesh from the object the script is on, with a shader that will flatten the verts. This will lead to only pure black shadows though unless you don’t mind a little overlap going on. you can then raycast to floor and render it there. We do a similar trick in one of our client titles.

If you have pro, you can route the DrawMesh results to a render texture then composite it into the main scene as an overlay with transparency you desire, and this will look pretty good.

A final suggestion would be that you render to a cubemap and blend that on the planet. This will take a bit of tricky maths to get right and a lot of fiddling but will allow you to correctly blend accurate shadows how you like on planetoids.

Thanks for the ideas, I will try them. Its too bad the Unity lighting system cannot be used.

If anyone else has any other ideas feel free to post here.

Thank you!

Check the documents for OnWillRenderObject(), when you move your directional lights forward vector in this function, i assume it will be like per object light.
I might be wrong, never tried it.

The thing is mobile supports just one directional light casting shadows, so that won’t work.

You didnt understand what I suggested. Anyways probably it wont work.

Yeah I reread now and I understand where you are coming from with this idea, but I doubt its going to work as you say.

Unity lighting system cannot be used anyway because the Shadow from the directional light renders on both sides of the Sphere using unlit shaders…Yet another issue I would have to circumvent.

Seems this is hopeless and will have to roll out my own system or maybe I’ll just hire someone to do that part. I’ll experiment and then we will see.

Thanks guys again for the help !