Shadow mapping with C++


, , , , , , , , , , , , ,

I asked the old man in rags, ‘Why is lighting so important in virtual reality?’

The old man took off a sandal, and waving it into the ether he replied, ‘OpenGL graphics library casts many different lights, say, directional light, point light, spotlight. Objects can be wrapped with diffuse and specular maps, to treat those lights. And then shadows, oh shadows!’

‘Yes,’ I snapped, ‘but why is lighting so important in virtual reality?’

The old man shook out the dust from his rags, and extracting a pipe he puffed, ‘Because it is important in the real world too. It’s just that other than sparking a candle we have little say. The sun and the moon cycle without our command. And the rays bounce across the lily pool as they please. But in virtual reality,’ his voice rose along with his index finger, ‘we are God!’

‘Fuck, you are right!’ I exclaimed, pushing the old man into the gutter, ‘Better get on with shadow mapping then.’

The Learn OpenGL tutorial Shadow Mapping has the detail. I cranked open my C++ Microsoft Visual Studio application (with OpenGL and the Oculus SDK for Windows) and implemented some shadows.

Here’s the view from our light space, using an orthographic projection:

It neatly fits our island into view for shadow mapping, whilst being close enough to beef up the resolution of said shadows. Bear in mind that the view is also distorted to work with a virtual reality headset.

An orthographic projection makes sense for directional lighting such as the sun. Nevertheless, here’s the same view, this time using a perspective projection:

Perspective projection is better suited to point lights, which have a fixed position in our virtual world.

Okay, time to don the Oculus Rift virtual reality headset and take a peek at the shadows cast by our orthographic projection:

As the Learn OpenGL tutorial explains, we first render our objects to a depth buffer (in our light space). And then we render our objects as normal (in our eye space), using the depth buffer to determine for each fragment whether it is in shadow or not.

The shadows we are casting from our grass are long – maybe the sun could do with being a bit higher in the sky? Plus they look quite blocky. We could consider increasing the resolution of our depth texture, since we can’t get the view from our light space any closer without chopping off bits of our island.

Anyway, here’s our shadows, this time using the perspective projection:

Since the grass does not move, I only need to create my shadow map once when the application starts. But what if we have a moving object whose shadow will change? In that case, we’ll need dynamic shadow mapping, where we create the shadow map each frame before rendering our objects.

Here’s a video of an axe chopping through the air, and its shadow slicing through the grass:

Performance is a consideration here. We are already rendering the scene twice for virtual reality – once per eye. Now we are rendering a third time, to create a dynamic shadow map. Works okay on my PC, but worth bearing in mind that extra processing.

There are further techniques in the Learn OpenGL tutorial to try out. For example, I had to apply a bias to avoid this shadow acne on the orthographic projection:

Look at those deep troughs of puss.

Other techniques to try include combating Peter panning, where objects become detached from their shadows due to the bias:

Thwarting over sampling, where some regions are considered to be in shadow when they’re not:

That shaded segment on the side of the building looks like a candidate.

And harnessing percentage-closer filtering, to tackle those shadows with jagged blocky edges:

I picked the old man out of the gutter and said, ‘Quick, see how important lighting is in virtual reality. Feast on the shadows.’

No sooner had I put the Rift on his head, he tore the virtual reality headset off. ‘Arghhh! It is a black magic most wicked. What devilish contraption is this!’

I bolted, with my PC and VR kit under my arm. And a chain of city guards in pursuit.

Gone was I. In a bolt of light.


The great thing about shadow mapping is that we can drop a couple of trees on the island and, hey presto, their shadows kick in without any extra effort: