Black Dragon is MY Viewer, i decide which feature i want to add and which to remove, i share this Viewer to show the world that user base size is not important, i do rate quality by effort, thought and love put into the project, not some rough estimated numbers. I consider feature requests only if i you can name proper valid reasons i can agree on. It is my (unpaid) time i'm putting into this project, i'm not here to cater to every Joe's desires.

Tuesday, April 2, 2019

Crawling Shadows: The Quest For Infinity

It's time i make a new informative post, one that i've made in the past already but will try to recycle in a more informative and hopefully better explained way.

This post will address the many issues with shadow rendering that many of you probably have seen already, one we might never ever have fixed.

So what is the issue? Let's start at the beginning, what are shadows, technically speaking. Technically speaking shadows are the lack of light or depending on how you see it a huge overay of darkness. In Second Life shadows are everywhere where there is no direct sunlight, how does this work? Take the sun, imagine your camera at this very moment is the sun and you are shining wherever you look. Now imagine you are shooting a ray from your sun camera into the world wherever you look, when the ray hits a solid object it stops and you count the distance how far this ray went before it stopped, everything past this ray in this direction is assumed to be shrouded in darkness, shadows. The Viewer does this many times every second, as fast as it can for every pixel on screen (with a 1920x1080 resolution this would mean 2073600 times just to finish the screen once). As you can imagine this is extremely slow (this is essentially raytracing) so to make this run decently fast some genius people came up with tricks to make this faster, one of them we use in Second Life. Cascaded Shadow Maps.

Cascaded Shadow Maps is simply put splitting the covered area into several segments (cascades) to make use of different shadow map resolutions for different distances, save resources and overall up the quality and reduce the artifacting we would encounter when rendering one large shadow map over the entire world. What we do is we take our "sun" and calculate a shadow map that is essentially a texture with three-dimensional information on lighting conditions in a covered area, we do this by rendering an image with coverage information with all objects inside this area, basically we are rendering the covered area a second time (although much faster and simpler) to "bake" the objects depth information into the texture which we later use to determine shadowing. We do this with a given resolution for each shadow cascade, 4 here (some games use 6). Since a shadow map can only contain a certain amount of depth information, similar to a depth map can only contain a certain amount of inbetween color steps from black to white, there's only a limited amount of precision, the smaller our area the higher the precision as we have more depth-steps in a smaller area, thus being able to define depth differences more accurately.

Depth map portraying the precision issue: Close to us everything is seemingly black (e.g the same depth) the same goes for flat angles and close to infinite depths, we only have 256 shades from black to white.

As you might guess this already has a massive drawback. If the resolution is too small, the area too big or the camera/sun angle too... special we run into precision issues.

Looking at the above pictures we can see that the shadow precision takes a huge hit on the lower one the further away our shadows are. My body still has highly precise shadows, while 4m into the picture the shadow precision becomes noticeably worse almost making it indistinguishable, another 8m into the picture and shadows become pixel mush. This is because the first 4m of the picture (roughly to the tip of my tail) the shadows use the first shadow map at 2048x2048 resolution, meaning we have roughly 5 pixels for 1cm depth, that is still highly accurate. The second shadow map uses 2048x2048 as well but covers 8m depth, meaning we are now left with roughly 2.5 pixels per cm. We've become half as precise and we can clearly see that 2.5 pixels is... just barely enough to produce something we can identify as our shadow. Beyond that we use 512x512 for the other two shadow maps, the third being 24m and the third being 64m. With 24m we get 1 pixel for roughly every 5cm that is highly inaccurate.

But wait that's not everything! We haven't even talked about the angles. BD has much better shadow accuracy but even BD has issues displaying shadows with low angle sun but why? Remember the depth map? Imagine you are looking at a plane from above, you can see every centimeter of it clearly, now as you approach a lower angle it becomes harder for you to see every centimeter of the entire plane, you start having issues telling apart how far something is away, if you were to lie down on the plane and look over the plane what would you see? Almost nothing, an almost straight plane, basically 0 depth perception, if anything or anyone were standing on it you could only guess how far it is away from you based on your previous experiences and its size. Similarly the rendering has issues defining depth on a plane that is pointing its ends towards you rather than the huge flat area, if you were to look perfectly along the plane on the same height as it you would see only the start of the plane and then the infinite nothingness behind it, 0 depth. Similarly here, if you were to shoot a ray or render an object in front of you while the camera is lying down flat on the ground you would only see the object, possibly a bit depth on the object itself if it does have some (like an arm in front of a body) and that's it, if you use this as shadow map you'd be casting a shadow from this object across the entire plane into infinity and the one pixel touching the ground would be used to determine whether the plane is shadowed or not for as long as this plane goes (obviously a super cataclysmic example), if we imagine the same situation with a slight angle we would still be using a single pixel for many centimeters (if not meters) of the plane causing extreme inaccuracies and artifacting... crawling, pixelation, flickering to name a few, the crawling here coming from the rendering applying this shadow pixel (square) to a changing camera view angle and guess what happens if you rotate a huge square shadow pixels to "face" your camera... right you see this pixels seemingly "crawling".

Example: How far do you think my avatar is?

Solution: You thought it was very close... no you thought it can't be because i wouldn't be asking this question otherwise right? You're right, my avatar is roughly at 1/3 of the plane's total depth. We can see this with the depth map, my avatar is clearly much brighter than the platform. This perfectly illustrates the issue with depth at certain angles, we simply can't tell depth here this creates mistakes.

How can we combat this? Raise the shadow resolution! Reduce the shadow distance! I hear you shouting... well yea... no. You see... this issue is just the drawback of how we render shadows, we cannot truly fix this, not without sacrificing more resources, whether it is adding more shadow cascades (6+) using extra techniques such as screen space precision shadows for small scale objects, raising the resolution or lowering the distance shadows cover, they all have their pros and cons, most noticeably cons, the performance. Raising the resolution does improve the accuracy but also massively impacts the shadow rendering performance, even more so with high complexity objects. Lowering the shadow distance does make the shadows more accurate but in a smaller area and can negatively impact performance as well in certain situations. Having a huge area covered is less straining than a smaller part of the same area that shows the most complexity since this complexity is now rendered with much higher precision (you could say resolution, but the resolution is the same its just more accurate). Adding extra shadow cascades uses more memory and adds 2 more shadow map rendering iterations to the mix and so on. All we can do is adjust the settings to match the scene as best as possible, this is why i added the shadow distance and shadow resolution sliders large non-detailed areas can be grouped into one shadow area with lower resolution than lets say an area with complex items such as grass/leaves or avatars which require a lot more accuracy to be distinguishable. A tip from me, lowering the first shadow map to 2 or 1 doubles/quadruples the accuracy of shadows extremely close to your camera while a 64-128m shadow map with 1024 to 2048 can be sufficient for a huge area, remember you can adjust all shadow maps separately to suit your current needs, make use of them.

The differences can be between this:

And this. I turned the shadow resolution close to me down and the distant ones way up, while the distant ones look decent now, the close ones have become a garbled mess.

Another example:

This is the transition from the second to the third shadow map, i could easily fix this by reducing the first two shadow maps (since they are not visible anyway) to 512x512 and the third (most of it here is covered by it) to 2048x2048 or higher.

1 comment: