Any way to make shadows non-additive?

Hello, folks. In my client’s 2D beat 'em up game, I produce shadows by duplicating the given sprite, inverting it, and manipulating the color/transparency. The problem is when we combine multiple shadows, for example with the player’s hand being layered on top so he can hold weapons. This is seen in the first image, where the hand shadow is floating just below the neck and creates a darker region than the rest of the shadow. In the second image the player is holding a guitar, which produces the same effect. (His hand would be casting yet another shadow but has not been split in this animation yet.)

We could get by in isolated cases, like by removing the hand from the original sprite for the first image. However, weapons make such an approach highly impractical. Is there a one-size-fits-all solution to suppress additive shadows? I suspect a shader trick is what I’m looking for, although I can’t be sure as I have no practical experience with shaders. Anyone pointing me in the right direction would be a huge help!

Could you possibly use the destination alpha channel? You’d have to enable 32-bit buffer on mobile… but then you can write all shadow values to dest alpha, like as 1.0, just overwriting what’s there, using a simple shader that sets the blend mode and to draw to the alpha channel only. Then draw a rectangle on top of it in a solid shadow color to blend with the background, only where dest alpha has values ie multiple dest alpha by the color.?

Unity Free way: Render shadows completely black slightly under floor… Make floor slightly transparent. This isn’t slow like you’d think - probably same performance since the same overdraw.

Unity Pro way: Render shadows completely black to shadow buffer. Floor has shader which reads the texture for shadows and blends them in.

1 Like

Hey, thanks guys. hippocoder, even though I have Unity Pro, I tried your first suggestion because it seemed the most straightforward. Very clever and easy to try, but either the shadow is too weak or the floor is tinted too noticeably.

As for all other suggestions, it’s obvious my lack of shader knowledge is too much of a handicap at the moment. I spent time this evening researching the topic, only to be dismayed at the scarcity of introductory materials. Even basic examples in Unity’s own manual left me feeling as lost as when I first tried teaching myself programming about 8 years ago. I managed to find a tutorial series on Unity Cookie with the level of structure I need, but I’ve barely scratched the surface.

I’m not expecting anyone to do my work for me, but if anyone is generous enough to drop some shader code, we’d certainly be grateful. :wink: In the meantime, I’ll advise my client to either find a shader programmer, or wait until I can catch up on the topic. The added flexibility would be nice, but this is the only use of shaders I foresee on this project, and I’m not expected to do shader work on any of my other projects. Finding a shader programmer is definitely our most efficient use of resources in this situation.

Well you could still do step 1 but render the floor under the shadow, then the shadow as full black. Then you finally render the floor as transparent over these, adjusting alpha. In this scenario, there is no colour information loss at all, and it only appears to affect the shadow transparency.

This would probably be same cost as using render textures anyway.

Side view:
------ transparent road
xxx shadow
------ solid road

Drawing order:
solid road with full black shadow → transparent road with alpha blend (not add or multiply). Adjust alpha of this.

Yeah, that initial suggestion worked perfectly aside from the tinting. I just applied this tweak - I’ll keep an eye on the performance, but it looks flawless! Thanks again. =D

No worries. Providing the shaders you use are mobile sprite ones or really lightweight it shouldn’t be a problem.

Yah nice workaround

If you want a quicker (but probably less optimal) solution to implement it in Unity Pro without shader programming, I’d do the following:

  • Make the shadows entirely black, and use a separate camera to render it to a different texture. Make sure the result you get is a black shadow on a transparent background.
  • Use this texture on a quad on the floor.
  • Make it transparent by using a pre existing shader which allows you to set the transparency value, or if you can’t find such shader, create a quad yourself with transparent vertex colors (using this in combination with a shader supporting this - which works with one of the built-in shaders if I’m not mistaken).
  • Note that you can set the vertex colors in code when you create your quad on the fly or when you edit an existing quad from code. This allows you to quickly experiment with various values (rather than having to go back and forth with an artist).

Thank you, bernardfrancois. Multiple background sprites interfere with the previous workaround, so we were in need of an alternative. We’ve found a specialist to assist us for this particular issue, but if for whatever reason it doesn’t work out, your solution is the first thing we’ll try.

Great. Would be nice if you could share the rough idea behind the final solution back to the community afterwards!

We ended up having too much trouble with our specialty shader. It broke too easily, especially given other parts of our workflow, and it was a hassle to configure when our original shadows were already great in terms of shape. Here’s confirmation that your solution worked, bernardfrancois! It’s ridiculously elegant and absolutely flawless in the end result (of making shadows non-additive). I even found an inspector script for specifying the draw order of the mesh in the exact same manner as a sprite. I didn’t think I’d be coming back around to your solution after this long, but thanks a ton!

I never mentioned it, but we wanted to skew the shadows in addition to this. It doesn’t appear to be practical with render textures, but it’s a reasonable compromise to skip skewing considering how much time we’ve already spent on the issue.

Hi Brian,

You can render your shadow shapes to the stencil buffer and then render a quad on top with stencil testing enabled to create uniform looking shadows.

1 Like