Rubinstein’s Fix for Depth Pass(Z-Pass) Shadow Volume.

Preface

In my new 3D mobile racing game Diesel Racer 2 I have decided to use Shadow Volume as the technique for generating shadows in real time.

There are several reasons why I preferred this technique over shadow mapping.

Shadow mapping requires render targets. Apart from taking more texture memory some older mobile devices suffer a lot in performance when using render targets(and using them as texture resource).

Another reason is that it would be hard to get high resolution shadow maps on mobile GPUs.

Perhaps on more powerful devices, but I aimed to target weaker devices such as iPod 4th generation.

How Does Shadow Volume Work?

You can read about Shadow Volume in this wikipedia page: http://en.wikipedia.org/wiki/Shadow_volume

In short, with Shadow Volume you would need to generate the geometry that contains the volume of the shadow cast by an object.

You do so by creating fins from the edges that connect two triangles. One triangle is culled from the point of view of the light source and the other is not.

The collection of all those culled/unculled edge fins would then contain the volume of the shadow the light source cast from this object.

You would also need the caps of the shadow in order to have a complete closed geometry of the shadow volume but the caps are not always required when rendering the shadow volume.

Now that we have the shadow volume we can find the shadows it cast on the object by counting how many front faced fins minus how many back faced fins we have on every pixel on the object’s surface.

We would only count the fins that are visible to the camera and are not occluded by objects, or in other words, fins that pass the Z-Buffer test(in a specific pixel in the view).

If there are more front facing fins than there are back it means that the pixel is inside the shadow volume and thus the object casting the shadow occludes the light source for this specific pixel.

 Z-Pass vs Z-Fail

The technique I described above is called the Z-Pass technique.

It works well enough unless the camera is inside the shadow volume.

In this case part of the volume fins are inside the viewing frustum and part of them are clipped.

Moreover, it may be that part of the fins are only partially inside the viewing frustum.

This will cause an under valued count of the amount of fins that are front facing the pixel and the amount of fins that are back facing.

One might suggests that you would add geometry on the front clipping plane of the frustum to accommodate for the fins that are behind the camera.

However, such geometry would be difficult to create especially since it’s rasterization might not sit perfectly with the partially clipped fin(with the front clipping plane of the frustum).

The reason this technique is called the Z-Pass technique is because we only count the fins that pass the Z-Buffer test or are not occluded by objects in the scene.

There is another technique called the Z-Fail(or “Carmack’s Reverse”).

Instead of counting the fins that pass the Z-Buffer test we could count the fins that fail the Z-Buffer test(the fins that are behind the objects in the scene).

The reason this works is because we are actually interested in the pixels on the surface of the scene’s objects and so it doesn’t matter much if we count the fins from one side of the surface or from the other side of the surface.

This will solve the issue of having the camera inside the shadow volume because Z-Buffer values are usually greater than 0(the front clipping plane{-1 for OpenGL}) and even if the Z-Buffer had values right on top of the front clipping plane, you wouldn’t see much anyway.

This technique requires that the shadow volume has caps in the end otherwise you would have ghost shadows since now the end of the shadow volume is more likely to be rendered(because it is more likely to be occluded by objects).

Rubinstein’s Fix for Z-Pass

We mentioned that in Z-Pass there is an issue when the camera is inside the shadow volume.

We also mentioned that we would could have fixed this issue if only we would create geometry on the front clipping plane that would negate the clipped fins or partially clipped fins.

However, that is not entirely true.

We don’t need to create geometry on the front clipping plane yet we can negate those back fins with no counterpart.

Lets say we have a closed mesh.

If we render this mesh with no Depth test(Z-Buffer test is set to always pass) and the camera is not inside it, then the front facing fins and the back facing fins will have to cancel each other and we would just get 0 as the total count of front facing fins minus back facing fins.

What if the camera is inside this volume?

Then we would get a negative count of all the missing or partially missing fins!

So in order to fix the issue of rendering inside a shadow volume with Z-Pass we would just need to render the shadow volume again but with no Depth test at all and adding the negative of the count!

The important thing to remember though is that in the fixing pass we need the shadow volume to be a closed shape(using caps).

Otherwise we would have ghosting shadows just like in the Z-Fail technique.

Broken Z-Pass Shadow

Broken Z-Pass Shadow

 

Rubinstein's Fix

Rubinstein’s Fix

 

Final Fixed Result

Final Fixed Result

Conclusion

The technique I presented could be an alternative to “Carmack’s Reverse”.

However, I am not sure how much more beneficial it is.

It probably has it’s own benefits and might be used for other things.

I am using this because it was easier for me to implement and because I wasn’t sure what was going on with the Z-Fail technique although now it’s easier for me to understand both.

In any case, I hope this article was beneficial for you.

p.s. Please don’t forget to check out Diesel Racer 2 on Google Play, Amazon, Apple Store and OUYA.

p.p.s. the Shadow Fix technique might not be available on all platforms yet, it takes time to update versions.

Edit:

I was told on r/gamedev that this technique is almost identical to “Carmack’s Reverse”.

While “Carmack’s Reverse” is doing Depth Fail I am doing Always minus Depth Pass which ultimately is equal to Depth Fail since A = DP + DF.

However, in my method I could render only a small part of the volume mesh in Always. Rendering it with DF would require to render the entire thing as you also need to render the shadows instead of only fixing the DP.

Here are the two meshes I use. One for the visuals and one for Shadow Volume ‘A’ fix.

Visual Mesh

Visual Mesh

Fix Volume

Fix Volume

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s