Friday, November 1, 2013

Colored Shadows in Unity

Sometimes you just want to colored shadows.  It may not be physically correct, but perhaps you're making a stylistic choice.  Unfortunately our options are limited.   Unity's Ambient Light is a global concept, so if you want to add colored shadows on a per-material basis for fine grain control, you probably need to implement it in your shader.

In order for our forest to feel more organic, I made some adjustments to all of our custom shaders - a very simple addendum within our lighting models.

Let's start with standard lambert irradiance.

c.rgb = s.Albedo * saturate(NdotL) * atten;

There are two things happening here, we're diminishing the color of the material by the diffuse component (saturate(NdotL)), as well as the combined factor of the surface's distance to the light source and whatever cast shadows are occurring (atten).  Both of these are 0->1 values.  This is what that looks like:

With all that black we lose a lot of contextual information.

Let's add some terms to our lighting to make it more interesting.

c.rgb = (s.Albedo * saturate(NdotL) + s.Albedo * _ShadowColor.rgb * (1-saturate(NdotL))) * atten
+ s.Albedo * _ShadowColor.rgb * (1-atten);

Since both the diffuse and attenuation/shadow components map 0->1 as "in shadow"->"in light", we can remap them, multiply against a per-material _ShadowColor, and add the result back into the mix.  Now colors that used to be crushed to (0,0,0) are (_ShadowColor.r,_ShadowColor.g,_ShadowColor.b), and our whites are preserved:

Per-material colored shadows for optimal control.

No comments: