Lighting the Pieces in Chess 2: Part Three (Shadows)

Features

The shadows in Chess 2 conform to the shape of the shadow caster, fade and become soft with distance. They also come from multiple light sources which adds to the ambient occlusion effect and makes the shadows colored slightly blue, to account for light scattering in the sky. Theoretically, they could even support refraction induced caustics - would the pieces be transparent.

Obviously, this is not possible in realtime on the OUYA. And, if you've read my previous post you won't be surprised to know that they are baked onto planes.

Constraints inform the method

The shadows in Chess 2 take advantage of from some constraints due to the type of game it is.

  • Chess pieces do not deform
  • Chess pieces do not rotate
  • Chess pieces move on the surface of a plane
  • Environment shadows are kept off the surface of the board

Because of these, it is possible to bake the shadows into planes on the ground, as long as some limitations are placed on the art, which will be discussed later.

Tech

The math for a diffuse shader in a forward rendering pipeline basically looks like this: a*albedo + b*albedo + ... n*albedo, where albedo is the color of the surface and a and b are light color * dot product of surface normal and light direction. We can factor this though to c*(a+b+..n). Since (a+b+...n) is precomputed when we bake we can just call it L.  So we simply have L*albedo. The important thing here is to see that the light can be simplified to a simple term L, and we can modify that term in the equation to change the light from it's current value to any other value we like by multiplying the final color, if only we know what to multiply by.

A multiplication texture is pretty simple to create. When baking the light, capture a lightmap to two planes. One will have a piece on it which is the light with shadow, and the other does not which creates a 'control' texture that informs us how much light is received where there is no shadow. With a program the shadow texture can be divided by the no shadow texture to get a texture of the term that the lighting of the board would have to be multiplied by to get the lighting of the board when affected by shadow.

All that is left is to somehow do the multiplication. One way would be to create a material using Unity's particle/multiply shader and apply the texture there. This shader has some bloat though, and we'll make a better one in the next section on performance.

This method for shadows makes shadows which are mathematically perfect in the context of this lighting model, support advanced lighting techniques like caustics and hdri lighting, and have low performance impact. Unfortunately it's only usable in a very small percentage of scenarios.

Art limitations

Because these shadows don't render on the pieces, but only on the flat board, the light direction of the sun was carefully chosen to center the shadows in-between squares, where they would avoid intersecting with pieces or other shadows.

Additionally, the board squares are rendered without a specular component.  When a specular component is involved, the light cannot be factored to a single term and the equation involves both addition and multiplication, so there is no value to multiply the final color by that will always have a correct result from all camera directions.

The scene is an outdoor scene to allow us to treat light sources as coming from infinitely far away.  Indoor lighting would not be possible because lights too close to the board affect the shadow direction

For the shadows to be perfect, the board should be a completely flat plane. In practice I think the added art benefit of having the individually cut squares outweighs the imperfection of the shadows.