How to Cheat Cheap 2D Parametric Drop Shadows (Soft / Hard) in Unity

When to use 2D Parametric Drop Shadows

In terms of performance: it would be best to add drop shadow to the sprite outside of Unity ,in photoshop. However, if you encounter special cases where parametric animations are required then Photoshop won’t cut it. An example would be if you need to have a sprite’s shadow react to environmental changes, or if the sprite is procedurally generated in-game and needs a procedural shadow to go with it.

Traditional 2D Drop Shadow Solutions: Problems with Performance

  • Render the sprite with multiple passes, like this sprite drop shadow shader: Drawing the texture in multiple passes would work and would look like a shadow, but this will affect performance and won’t look that good for thicker outlines with out a lot more passes. Multi-pass shader breaks batching and should be avoided on mobile.
    • Draw each sprite twice, the first normally, the second with some kind of drop shadow shader a bit deeper in the scene: the blurring will have to be done inside the shader and might involve multiple texture lookups or Gaussian blur, which is very taxing on performance.

Cheat Solution: Cheap 2D Drop Shadow using only 1 Pass

This drop shadow is done in a single pass and just needs 1 extra sample, it does not break batching and is suitable for use with atlasing – this is suitable for mobile with minimal impact on performance.

The method involves:

  1. Sample the texture alpha using X & Y UV offset to create the basis of the shadow silhouette shape
  2. Color the shadow silhouette shape using a custom shadow color property, multiply it with (1 – texture.a) to make sure the opaque texture areas are not affected.
  3. Use max(shadowAlpha, texture.a) to prevent the shadows from bleeding into the opaque texture areas.

What about Soft Shadow support?

To create softness: Simply sample the alpha from the texture’s mipmaps. Make sure to turn on “Generate Mip maps” in the texture settings. Create a slider in the shader to control different levels of blurriness based on the mip map level.

Tip: you might want to create extra padding in the atlas for the shadows in certain scenarios, if you want deep shadows to work correctly!

Example Code:

(This is an URP example. To convert to Built-in Pipeline, replace “SAMPLE_TEXTURE2D” to “tex2D”)

half4 mainColor = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uv) * IN.color;
half shadowAlpha = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uv + half2(_ShadowX, _ShadowY)).a; 
half4 shadowColor = _ShadowColor * shadowAlpha * (1 - mainColor.a); 
mainColor.rgb += shadowColor;
mainColor.a = max(shadowAlpha * _ShadowAlpha * IN.color.a, mainColor.a);

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s