I struggled getting shadows working, there was a real moment where I wanted to cut my losses and give up.

It was particularly frustrating because shadow mapping itself isn’t a difficult concept to comprehend.

You render a depth map from your shadow-casting light’s perspective, capturing what the light sees–and equally importantly, what it doesn’t. Then, while rendering pixels (fragments technically) during the main render pass, you check if that pixel falls within the light’s bounds (based on clipping, range, falloff, etc.). If it does, you transform that fragment’s world position into the light’s space and sample the shadow map. You compare the fragment’s depth (from the light’s perspective) against the stored depth in the shadow map–if the stored depth is less, something closer to the light is blocking it, so that pixel is in shadow.

Clear as mud.

I didn’t get this far with the Vulkan renderer, so I imagine that’s why I found this difficult. I hadn’t realised how much my previous experience has taken the edge off this project to date.

Despite many resources out there on the topic123, I spent a full day (at least… I’m not tracking my time very well at the moment) dismantling and rebuilding systems that were working fine, and a couple more figuring out what my problems actually were.

A picture is worth a thousand words, so here are some screenshots…

What am I looking at?

What am I looking at?

Not even the right direction.

Not even the right direction.

Clipped into oblivion.

Clipped into oblivion.

Uhh.

Uhh.

Debugging…

Debugging…

Finally a shadow! but that cube shouldn’t be black.

Finally a shadow! but that cube shouldn’t be black.

At the risk of glossing over things, my issues were quite simple

  • Poorly defined light near/far ranges causing either nothing or everything in shadow
  • Shadow lights uniform buffers sharing coordinate spaces
  • NaNs in shaders causing glitches

So, after working through that I now have a functional ShadowRenderer which can render shadow maps for up to 8 shadow casting lights.

  • l toggle shadows
  • f reset camera
  • +/- increase/decrease locator scales
  • t toggles debug locators
  • m toggles MSAA
  • ~ opens the terminal
  • Mouse look with left/right mouse buttons
  • w, a, s, d to move
  • p to pause

So where to from here?

I still have shadow map work to do, right now they’re being rendered every frame (required for lights or shadow casters that move) but it would be nice to be able to bake a shadow map once and reuse it for static scenes.

I also found a nice resource4 on SSAO (screenspace ambient occlusion) which doesn’t look like much extra work. That would also pave the way for other screenspace techniques like reflections, DOF (depth of field) and atmospherics. As well as NPR (non-photorealistic rendering) techniques like cell-shading, so that sounds like a neat challenge.

I also have a lot of work to do on materials. Right now I just have the Lambertian model implemented, but I want to support PBR (physically based rendering) materials and phong or blinn for simple specular. I haven’t even begun to look at transparency, though I have touched on this many years ago. I remember it being a real pain.

In terms of engine progression, scene management has become a problem. While thinking about this problem space, I’d like to research using an ECS (entity component system) so I can assetize things properly. I imagine this will involve more restructure/refactoring.

I’ll have to see what grabs me!