Skip to content

Shadow Map Atlasing

N8n5h edited this page Feb 15, 2021 · 17 revisions

Shadow Map Atlasing

Introduction

  • What are shadow maps

Like most modern game engines, Armory employs shadow maps to draw shadows in a scene. This technique, simply put, it's the process of utilizing images, known as depth maps, to determine what it's lit and what is occluded when rendering the final image. This technique it's really efficient to draw shadows but it has a few different problems and limitations.

  • Limited dynamic scenes

One of the problems with shadow maps is that it's difficult to optimize it for a considerable number of lights in a way that doesn't limit a scene's dynamism. Relatively new and recent graphics API versions do offer tools to get around this problem, but relying on them would mean losing support for old targets just to solve this. Instead, another approach is to use a technique called shadow map atlasing. This was paired with clustered shading to solve the original problem in a different way.

  • What is shadow map atlasing and why it was chosen

Atlasing can be defined as the process of grouping multiple textures into one or several big textures. For this case, atlasing of shadow maps refers to the process of rendering different shadow maps into the same big texture or shadow map atlas.

This approach has a few benefits:

  • First and most importantly, it solves the limitation described earlier by simply getting around it, since for the shader part the atlas is just a simple texture which can be handled normally.
  • Secondly, it may see a performance increase since the number of texture switches it's considerably reduced.

Yet atlasing has a few flaws that are worth mentioning just for completion:

  • In order to determine the UV coordinates in the atlas a few GPU cycles are wasted. This gets worsen when dealing with point lights, which requires transforming from multiple different faces.
  • No wrapping textures. This complicates determining some UV coordinates for point lights.
  • A complicated code solution may be needed to manage the atlases if it's expected to keep lights and scenes dynamic.

How to use shadow map atlasing in your project

  • Using shadow map atlases

Armory offers a code solution to handle shadow map atlases out-of-the-box that when enabled will automatically take care of the management of lights that affect the active scene and consequently their shadow maps. Because of the dynamic nature of scenes, it requires taking some considerations when lighting the scene, mostly because it's expected that the code solution don't impose certain limitations on the scene from the get-go.

NOTE:

This feature it's relatively new, and because of that it is disabled by default to provide some stability for projects before it's tested enough. For now you have to opt-in in its usage, and disabling it completely removes the code related to it when compiling.

At the moment, it's confirmed to work with Krom Windows and Krom Linux. HTML5 was tested and it doesn't work with multiple lights, which introduces artifacts in the shadows. See https://github.com/armory3d/armory/issues/2110

To enable it you have to activate the toggle button Shadow Map Atlasing that it's located under Render Properties > Armory Render Path > Shadows.

how to enable the option

Activating it will make available a few different options to better optimize it for the needs of your project and export target, which are explained below:

  • Max Lights

This sets the limit of how many visible lights will be rendered at the same time. This value is used for the uniform count related to lights, so it's worth to keep this in mind when targeting devices with a low number of uniform count, like android devices.

  • Max Lights Shadows

This sets the limit of how many shadow maps will be rendered at the same time. The same than Max Lights applies to this constant.

  • Shadow Map Atlas Single Map

This feature allows choosing between a single shadow map atlas, or multiple atlases. It may save some performance since there will be just one texture, with the downside of requiring a bigger atlas size to hold the same amount of lights in the contrary if they were separated. Useful if you don't have many lights and want to save as much performance as possible.

  • Max Atlas Texture Size - type

These values define the max size of atlases.

The atlas is subdivided in what is known as tiles. tiles have the size of the shadow map size (cascade size for spot and sun, cubemap size for point).

Simplified visual representation of an atlas

Every time a new lights needs to be added to the atlas, a number of free tiles will be assigned to it if there are enough available. Under certain conditions a light will be removed from the atlas and the space it used will be freed to be used by other lights that need it. Every frame it's checked if lights can be added again to the atlas if all conditions below are met: The light object is not in an atlas already, the light has its data.raw.visible attribute set to true, light has its data.raw.strength attribute set to be greater than 0.0, light has its data.raw.cast_shadow attribute set to true and finally the light object it's inside the view frustum.

The tiles needed to add a light to an atlas vary depending of the type of the light and cascades setting:

Spot lights Point Lights Sun
tiles required 1 6 Cascades

A simple case to understand what limit to set:

Assuming there are 4 point lights visible at the same time at a given moment in a scene, then 24 tiles will be needed to render the shadow map atlas. If single map is disabled and the cubemap size is 512, it will require an atlas size of 2560x2560. So the limit for point light atlas should be at least 4096x4096.

It's worth noticing that point lights require much more space than other type of lights because of the nature of how their shadow maps are rendered. LOD can help with this to hopefully reduce the space needed, but it depends on the distribution of lights in the scene.

If not enough tiles could be found because the limit has been reached, the light will be skipped from getting in the atlas and having its shadows rendered until there is enough free space. It is important that this limit is set carefully, mostly because it depends on the hardware limitations imposed by the target you will publish to.

Warning: If the target device has not enough video memory for the atlas size, the game may crash or soft lock.

  • Shadow Map Atlas LOD (Experimental)

This feature enables Level Of Detail for shadow maps, which is an experimental feature that it's intended to save space and performance by dynamically changing the size of the required shadow map based on the distance to the camera view frustum. Explained simply: the closer a light is to the camera, the higher the resolution required.

The default shadow map size (cascade size and cubemap size) is the biggest size a shadow map can be, and the smallest size depends on the LOD Subdivisions. The highest detail is located at near plane distance of the view frustum (offseted to the front a little because of the clustering algorithm) and it ends with 0 in the far plane.

Details worth mentioning:

  • If there is not enough space for a bigger shadow map size, a light will remain with the same shadow map size it currently has until the next check the next frame.
  • All tiles that a light require will be of the same size.

simplified visual representation of how LOD works for shadow maps:

Simplified visual representation of Frustum View LOD and Culling

It's worth noticing how out of view lights have 0 as their shadow map size. This, referred in the solution as light culling occurs regardless if LOD is active or not. light culling is related to the clustering algorithm, and depends on the calculated radius based on the strength of the lights. This means that the chance of culling a light that is outside of view depends on the strength of the light: The bigger the strength the farther from view it has to be to be culled. This has to do with how the clustering algorithm works.

Also be aware that view culling is not the same as occlusion culling, so all lights that are inside the view culling are rendered, regardles of if they are completely visible to you and if they even affect the lighting of the scene at all. This is important to keep in mind because lights that are occluded still affect performance at the moment.

  • LOD Subdivisions

This option allows controlling the number of subdivisions of LOD, for better personalization of it. The ideal configuration depends on how far the far plane distance of the camera is, and/or if there are some noticeable pops in quality because of it. Be aware that the more subdivisions the more performance it may take to handle them, because of how the algorithm to find free tiles work.

Debugging

At the moment there isn't much tools to debug atlasing inside of armory, but this is expected to change in future iterations of this feature. For now you can see in the debug console if a light is being rendered or not, by their status: culled / visible. Renderdoc may also help understanding if there is an error with shadows for desktop targets.

Clone this wiki locally