Swift Shadows is a lightweight substitute for Blob Shadow Projector, heavily optimized for mobile devices. It works by casting a ray to the surface and drawing a transparent quad that hovers above it. Literally hundreds of shadows can be drawn on a mobile device with a single draw call.
Works on any platform, does not requires Pro license to work. Heavily optimized for mobile.
Supports shadow texture atlases. All shadows in the scene can be drawn in a single draw call, even when they have different shapes.
Tons of customization options.
Shadows match object rotations.
“Static” shadows that are calculated only once and produce no overhead in run-time. It is possible to have hundreds of static shadows without a performance hit even on mobile devices.
Shadow generator tool for creating nice looking shadows automatically.
Sprite packer tool for easily packing multiple shadow textures into one to reduce the amount of drawcalls.
Ease of use, absolutely no scripting required.
Full source code and useful examples.
Certain limitations exist:
Shadow textures are static. Shape of a single shadow is not updated in real-time. In some cases it is possible to work around this by using animated shadow cookie or by using multiple shadows.
Each individual shadows only projects on one surface at a time. This leads to a discontinuity as shadow moves from one object to another.
Shadow can extend the edges of object it is being projected on. It can also clip inside complex-shaped objects.
These peculiarities are rarely noticeable for moving objects with blurry edges.
Plugin is tested in Unity 4.6.x - 2017.x. Pro license is not required. All platforms are supported.
Support:
For any questions regarding this plugin, feel free to contact me at contact at lostpolygon.com or post in this thread.
Great Asset. I searched the asset store for a fast shadow tool that works with Unity Indie. I picked yours because you have the shadow material generator.
Everything works great. I had shadows applied to my character in less than 10 minutes.
Only one slight glitch. When my character is on the ground, it is not projecting a shadow? Any suggestions on what might be causing this glitch?
this happens because the shadow is projected from the position of the GameObject, which in case of characters is often the lowest point in the model. Because of that, the raycast goes under the ground, not hitting anything. The fix is simple:
Create an empty GameObject
Attach it as a child of your character
Position it to the (0, 0, 0), then move it up roughly to the middle of your character
Add SwiftShadows script to that empty GameObject (removing the SwiftShadows script from the character object itself).
I have a situation where an object needs to cast a shadow but is created during runtime, and never moves after (but can be destroyed). Is it possible to mark them as static and therefore they don’t need further shadow calculation? So basically, does your plugin support static shadows that are created at runtime?
I just want to share some code for automatically changing the light source to the closest light. This is usefull if you aren’t using a directional light and have lots of lights in your scene.
You will need to tag all your lights to find them. I am using the tag “LightsTag”
I have SwiftShadow on a child object, “AnchorPos”. You will need to adjust this line for your setup.
I also have a mask on the raycast. You can remove this, or setup for your environment.
Then just attach this script to your character.
We don’t need to check for a change in light source every update call. Note the variable, updateLightShadowFrames. The higher the value, the less often the script will check for a change in light source. This makes it less costly.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class ShadowFollow : MonoBehaviour
{
// Class Variables
public int updateLightShadowFrames = 50;
private List<Transform> Lights = new List<Transform>();
private string LightsTag = "Lights Tag";
private int updateLightShadowFramesCount = 0;
private SwiftShadow _shadow1;
void Start()
{
// Get all lights
GameObject[] go = GameObject.FindGameObjectsWithTag(LightsTag);
foreach(GameObject g in go)
{
Lights.Add(g.transform);
}
// Get shadow script
_shadow1 = GameObject.Find("AnchorPos").GetComponent<SwiftShadow>();
}
void Update()
{
UpdateShadowLight();
}
private void UpdateShadowLight()
{
if (updateLightShadowFramesCount++ > updateLightShadowFrames)
{
// Reset counter
updateLightShadowFramesCount = 0;
float closestDistance = float.MaxValue;
int lightId = -1;
// Loop through all lights to find the closest
for (int i = 0; i < Lights.Count; i++)
{
float distance = (transform.position - Lights[i].transform.position).sqrMagnitude;
if (distance < closestDistance)
{
// Cast a ray to find out if light can reach player
Vector3 direction = transform.position - Lights[i].transform.position;
RaycastHit hitInfo = new RaycastHit();
Physics.Raycast(Lights[i].transform.position, direction, out hitInfo, 1000f, PlayerController.Instance.Laser.MaskTargets);
if (hitInfo.transform)
{
// if we hit player then this light is closer
if (transform == hitInfo.transform)
{
lightId = i;
closestDistance = distance;
}
}
}
}
// If we found a light, then update shadow
if (lightId > -1)
{
if (_shadow1.LightSourceObject != Lights[lightId].transform)
{
_shadow1.LightSourceObject = Lights[lightId].transform;
}
}
}
}
}
solved, my textured shadows just needed a swift shadow shader in the material and somehow, after reapplying the quality settings, also the default blob works ok; I suppose I have just messed too much with the settings
for what I have seen, your product is nice, perfectly encapsulated and easy to use!
How many garbage are you getting? There are two “sources” of garbage in Swift Shadows:
The first one is just 28 bytes per frame, it’s nothing to worry about even on a ancient device like iPhone 3GS. The second one is bigger, but it does not happens in the build, it exists only in the Editor, so it is not a problem too. So actually, only 28B of garbage per frame will be generated in the actual build, and even that isn’t worth worrying about.
Only the shadow generator tool is put inside a dll, everything else is in the source form.
In your demo, there is a “use two cameras” option. Can you explain this? It isn’t in your documentation.
I am working on a vehicle driving game. And while on road sometimes shadow gets clipped if road isn’t planar, on terrain it’s worse. I need to use that option, but would it affect performance?, if it would, how bad would it be?
Also, how can i make a texture atlas? Does swift shadows auto generate asset? Or should I make it manually?
Well, I after checking the demo, i have made a secondary camera, made it show only ignore raycast, set shadow to ignore raycast layer. And removed ignore raycast layer from main camera. And then I’ve set the depth.
But right now shadow is “on” the car, it’s in front. How did you workaround that in that demo? Thanks.
“Use two cameras” isn’t really an option in Swift Shadow, it is just a way cameras are set up, as you have discovered. You got it almost right, except this: your car must be in the same layer as the shadow. In the demo, character has “Ignore Raycast” layer, and the shadow is just attached to it as a component, with “Shadow Layer” set to “Same as GameObject”. This technique indeed has some performance hit, because the secondary camera has to clear the depth. However, that is a really cheap operation. I’ve just did a quick benchmark and found zero difference in actual performance on my device.
Texture atlases are not generated automatically, so yes, you have to do that manually at the moment. The atlas in the demo is simply 2x2 square cells, so it isn’t really hard to do it manually. And of course, you can generate atlas with some external tool.