Can 2D Shadow Caster use current sprite silhouette?

Since i have to setup manually the Shadow Caster shape, it’ll work fine for static objects but looks weird on an animated character sometimes, specially in cases where there’s multiple directions and each sprite direction have a somewhat unique silhouette.
Is it possible for the 2D Shadow Caster use the Sprite pixel instead?

1 Like

Hi !

Same problem here, did you find a way to make it works in the meantime ?

I’ve spent a lot of time working on this today, here’s the short answer: there’s no good way at this time. Or if there is, I’m not able to see how really. The reason is that the ShadowCaster2d ShapePath is currently a private field. You can do some… really hacky things to get closer to your goal but nothing like using the non-alpha mask on an image to cast shadows (yet) afaik.

I was initially planning to use a preprocessed shape and just apply a polygon that way. I was intending to use this in the context of an animated sprite, so you’d need to update the polygon pretty often.

I’ll show you what you … sorta can do. But this is not really all that great, and not performant, but it does sorta work.

Since you can’t really change the shadow shape programmatically (At least I couldn’t do it) you can use System.Reflection to get the FieldInfo like this

private ShadowCaster2D shadowCaster;
private static BindingFlags accessFlagsPrivate =
    BindingFlags.NonPublic | BindingFlags.Instance;
private static FieldInfo meshField =
    typeof(ShadowCaster2D).GetField("m_Mesh", accessFlagsPrivate);
private static FieldInfo shapePathField =
    typeof(ShadowCaster2D).GetField("m_ShapePath", accessFlagsPrivate);
private static MethodInfo onEnableMethod =
    typeof(ShadowCaster2D).GetMethod("OnEnable", accessFlagsPrivate);

Then in your Start() method you can do something like this

shadowCaster = GetComponent<UnityEngine.Experimental.Rendering.Universal.ShadowCaster2D>();
var somePathYouLoadedMaybe= new Vector3[]{
  new Vector3(0,0,0),
  new Vector3(0,1,0),
  new Vector3(1,1,0),
};
shapePathField.SetValue(shadowCaster, somePathYouLoadedMaybe);
meshField.SetValue(shadowCaster, null);
onEnableMethod.Invoke(shadowCaster, new object[0]);

Here is that code running, makes a simple triangle:
5950889--637517--upload_2020-6-7_17-35-26.png

I attempted to update this polygon with different values in the Update() & FixedUpdate() Methods and it basically made the game unplayable and slow.

I am definitely curious to see if others have figured out something of more substance here.

1 Like

That’s a great suggestion regarding the use of sprite silhouettes for the Shadow Caster 2D! Thank you! It’s certainly worth for us to look into. Let me feedback it to the 2D team.

1 Like

All this nonsense, because we are trying to come up with work arounds which is understandably in experimental stage.
However, it doesn’t mean that the demand isn’t there for more robust update with 2DRenderer. In fact, it is the otherway around. I think there is high demand for the more frequent, robust update plan for 2DRenderer so that devs can plan ahead around the features it will bring/change. We can understand that we may have to wait, it is just that we don’t know how long, for what features if any. I don’t have problem with delay, but I do have issue with not knowing the plan, estimated schedules, information on what features to be implemented etc… bottom line is the communication. Unity can all of sudden bomb us with “here is this new feature, just released it now go get it” but ideally, and unfortunately that is not the most of devs wants.

2 Likes

Thank you for your feedback, Castor! We definitely hear you there and understand your frustration of not knowing the upcoming features and their estimated schedules. I’ve already feedbacked this to the teams and it has been escalated to the appropriate members. We are now working towards greater transparency in our roadmaps so that our users would be the first to know of these new features and would also be able to share our excitement as we work hard towards the next releases. We will keep you posted on any updates as soon as we have them!

1 Like

Having worked at the large corp before, I do understand the fear of exposing the schedules and plans ahead because frankly anything can happen and the promise may not be met. But I feel that the community of Unity developers are much more forgiving and understanding than usual software retail customers because we ourselves are the developers. All we really want is to be able to ride the adventure together. Share the plan, share the dream, and we will rally behind it.

4 Likes

Yeah we are so glad you feel this way too! Anyway, adding on to what happened after I raised your suggestion to my team yesterday - they really do feel the need to make that public roadmap happen where we could show you guys the upcoming features, their statuses and the dates of our releases in advance. So they initiated to amplify your suggestion to the higher-ups first thing this morning today. We are really hoping to push something out and we are excited to see what we can do to make that happen.

3 Likes

Hi, sorry to revive a dead thread, but this feature is also very important to my project. Almost all of our assets are loaded at runtime, and it would be near impossible to define the sprite shapes for the shadow caster like this.
Even an inaccurate shadow based on the pixels would be useful.

Has there been any news of a roadmap so I can watch for a feature like this? Thank you very much

Yes! The discussion regarding 2D Shadow Caster using the Sprite pixel data has come up a few times now. More updates on this soon.

It’s not ready yet as far as I know but there has been initiatives to set one up soon.

4 Likes

Using the reflection method @the_Simian suggested I wrote a helper class that can do this at runtime. @Saafris might be useful for you.

The reason Simians approach becomes unplayable is because its infinitely generating meshes, if you watch your vert/tri count in the game window it’ll keep getting larger until the FPS tank.

The ShadowCaster2D class already forcefully re-generates its mesh in Update() when the shapeHash changes, I use reflection to set the points and then set the hash so the mesh rebuilds. Im not particularly knowledgeable about hash codes so I copied how its done for Sprite Shape Splines.

Heres the full class, just add it to the same object as the ShadowCaster2D.

Call UpdateShadowFromPoints() and pass it the array of points whenever you want the shadow to change or just call UpdateFromCollider() and it’ll use the collider points instead.

using System.Reflection;
using UnityEngine;
using UnityEngine.Experimental.Rendering.Universal;

[RequireComponent(typeof(ShadowCaster2D))]
[ExecuteInEditMode]
public class ShadowCaster2DController : MonoBehaviour
{
    [SerializeField] private bool setOnAwake;
    [HideInInspector, SerializeField] private ShadowCaster2D shadowCaster;
    [HideInInspector, SerializeField] EdgeCollider2D edgeCollider;
    [HideInInspector, SerializeField] PolygonCollider2D polyCollider;

    // Shadow caster fields to change
    private readonly FieldInfo _shapePathField;
    private readonly FieldInfo _shapeHash;

    private ShadowCaster2DController()
    {
        _shapeHash = typeof(ShadowCaster2D).GetField("m_ShapePathHash", BindingFlags.NonPublic | BindingFlags.Instance);
        _shapePathField = typeof(ShadowCaster2D).GetField("m_ShapePath", BindingFlags.NonPublic | BindingFlags.Instance);
    }

    private void Awake()
    {
        shadowCaster = GetComponent<ShadowCaster2D>();
        edgeCollider = GetComponent<EdgeCollider2D>();
        polyCollider = GetComponent<PolygonCollider2D>();

        if (!setOnAwake) { return; }

        if (edgeCollider != null)
        {
            UpdateShadowFromPoints(edgeCollider.points);
        }
        else if (polyCollider != null)
        {
            UpdateShadowFromPoints(polyCollider.points);
        }
    }

    /// <summary>
    /// Updates the shadow based on the objects own collider
    /// </summary>
    public void UpdateFromCollider()
    {
        if (edgeCollider != null)
        {
            UpdateShadowFromPoints(edgeCollider.points);
        }
        else if (polyCollider != null)
        {
            UpdateShadowFromPoints(polyCollider.points);
        }
    }

    /// <summary>
    /// Updates the shadow from an array of Vector3 points
    /// </summary>
    /// <param name="points"></param>
    public void UpdateShadowFromPoints(Vector3[] points)
    {
        // Set the shadow path
        _shapePathField.SetValue(shadowCaster, points);

        unchecked
        {
            // I have no idea what im doing with hashcodes but other examples are done like this
            int hashCode = (int)2166136261 ^ _shapePathField.GetHashCode();
            hashCode = hashCode * 16777619 ^ (points.GetHashCode());

            // Set the shapes hash to a random value which forces it to update the mesh in the next frame
            _shapeHash.SetValue(shadowCaster, hashCode);
        }
    }

    /// <summary>
    /// Updates the shadow from an array of Vector2 points
    /// </summary>
    /// <param name="points"></param>
    public void UpdateShadowFromPoints(Vector2[] points)
    {
        // Set the shadow path
        _shapePathField.SetValue(shadowCaster, Vector2ToVector3(points));

        unchecked
        {
            // I have no idea what im doing with hashcodes but other examples are done like this
            int hashCode = (int)2166136261 ^ _shapePathField.GetHashCode();
            hashCode = hashCode * 16777619 ^ (points.GetHashCode());

            // Set the shapes hash to a random value which forces it to update the mesh in the next frame
            _shapeHash.SetValue(shadowCaster, hashCode);
        }
    }

    /// <summary>
    /// Converts an array of Vector2 to an array of Vector3
    /// </summary>
    /// <param name="points"></param>
    private Vector3[] Vector2ToVector3(Vector2[] vector2s)
    {
        Vector3[] vector3s = new Vector3[vector2s.Length];

        for (int i = 0; i < vector2s.Length; i++)
        {
            vector3s[i] = vector2s[i];
        }

        return vector3s;
    }
}
1 Like

Has this ever been implemented?

Hey @vambier_1 , sorry for the delayed response.

Yes our 2D Public Roadmap is now up and running for all five areas of our 2D feature-set: 2D foundations, world-building, animation, graphics and physics.

Also, regarding the request for 2D Shadow Caster to use the Sprite pixel, that is being prioritised and someone on our team is already looking into it. So we will be posting more updates on our 2D Public Roadmap soon!

4 Likes

Is this implemented yet? I can’t find it in the 2d roadmap

Not yet but works on the shadow interoperability are already in R&D and are well underway in collaboration with the respective 2D feature areas.

Any update on the sprite silhouette for the shadow caster? On the roadmap, we can see what has been released but not what is working on…

The major changes for shadows for 2023.1 are:

ShadowCaster2D now can use sprites (including 2d animation, and flipbook), sprite shape, and colliders, or custom geometry as a source.

2D Soft Shadows

5 Likes

Hopefully that also includes shadow falloff distance.

23.1 doesn’t. I want to add z for shadow casting at some point. That will do what I think you want, but in case it doesn’t, do you have a screenshot of what you’d like to do?

I

Is this already available on some 2023.1 beta version? I’d really like to try it ou to see how it is working