
Hello again!
It’s been just over 2 months since my last post. Back then I mentioned my (day, real, paying) job was occupying pretty much every brain cell I had, so I decided to park SR while I worked through that.
It was never far from my thoughts though.
Last week I jumped back in to take a look at something that had been lurking in the deep recesses of my mind.
Shadows.
Yep, shadows again.
Specifically, shadows cast by point lights.
Point Light Shadows
For the sake of shadow mapping, there are only two kinds of lights, directional, and omni-directional. A directional light covers spot and directional lights and omni-directional lights (also-known-as point lights) are a point in space where light is being emit in all directions.
Shadows for a point light are a bit more complicated.
As a quick refresher, to calculate shadows for directional lights, you render the scene from the perspective of the light into a depth map (a.k.a shadow map), then as you calculate the light contributions from the camera, you compare the position of a pixel against the shadow map (projected from light space) and if that depth test is less than what was recorded by the shadow map, something is blocking it and it’s in shadow.
If you’re interested, I go into more detail here and here.
Anyway, the problem is a simple one, because omni-directional lights don’t have a direction a single shadow map won’t do.
The solution is to use an array of depth maps, six 90° maps per light (-x, +x, -y, +y, -z, +z).
As you can imagine, this makes calculating shadows for an omni-directional light a fair bit more expensive. So while you always want the option, you typically use them sparingly in a real-time application.
Bugs Ahoy
While working on this…
- I realised that I had broken shadow mapping for the
Directionallight, and only theSpotLightwas working. So that is fixed. - I had added a
castShadowsoption for both lights and meshes but hadn’t done anything with it, so that’s implemented now. - I noticed I was doing some matrix math per-entity, per-frame when it could be done once per-frame for all entities. That’ll be a bit of a performance boost in scenes with lots of entities.
- My Frustum class was using OpenGL conventions, I guess I had forgotten to convert this when it was implemented and it wasn’t obvious, so it went unnoticed until now.
- Shadow-caster culling is handled properly now (I think…).
To that last point, previously I was using the camera frustum to cull everything. This causes a problem I was aware of conceptually, but hadn’t bothered looking into.
If you cull an object that is casting a shadow, that shadow disappears with it.
My approach now is to cull objects per light volume, specifically for shadow maps. So the camera frustum cull takes care of the main render passes, but not for the shadow maps. This should mean point light shadows are a little more efficient when they’re they don’t have in-range shadow casting objects in one or more of its 6 directions.