Skip to content

Shadow Map Atlasing

N8n5h edited this page Apr 25, 2021 · 17 revisions

Shadow Map Atlasing

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. By keeping it disabled, the code related to it gets completely removed when compiling.

Sections

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. It's a really efficient way to draw convincing shadows but it has a few different drawbacks 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. Newer versions of graphics API(s) offer tools to get around this problem at the cost of losing support of older targets.

So instead, an alternative is to use a solution called shadow map atlasing, which paired with clustered shading allows solving the original problem without updating.

  • 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. In this case, related shadow maps get grouped together 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 an UV calculation and a simple texture lookup.
  • Secondly, it may see a performance increase since the number of texture switches it's considerably reduced, depending on the target.

Yet atlasing has a few flaws that are worth mentioning:

  • In order to determine the UV coordinates in the atlas a few GPU cycles have to be wasted. This gets worsen when dealing with point lights, which require custom cubemap look ups.
  • No wrapping textures. This complicates determining UV coordinates and computing PCF for point lights.
  • A complicated code solution may be needed to manage the atlases if it's expected the scene dynamic.

How to use

  • Enabling shadow map atlasing

Armory can handle shadow map atlases out-of-the-box for you by simply toggling the Shadow Map Atlasing option in Render Properties > Armory Render Path > Shadows.

how to enable the option

When the option is enabled, Armory will automatically take care of the management of lights that affect the active scene and consequently their shadow maps.

Caveats

To keep scenes dynamic, there are a few caveats that you need to be aware when designing your scene light placement.

Configuration

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 some mobile 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.

  • Shadow Map Atlas Single Map

Group all lights into one single atlas.

This reduces texture look ups and reduces slightly the overhead of the shadow map handling at the cost of losing overall space for many lights. Ideal if you don't plan to have a lot of lights in your scene and want to save as much performance as possible, while also keeping atlasing enabled.

  • 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

Free tiles will be assigned to lights whenever needed if there are enough available.

Under certain conditions a light will relinquish their used tiles, which allows reusing them. If some conditions are met, the light will be added again to the atlas.

The conditions are as follow: the light is visible (data.raw.visible), it has a strength (data.raw.strength) > 0.0, it can cast shadows (data.raw.cast_shadow) and is visible to the camera (with a few caveats explained later).

The tiles for a light vary depending of the type of the light and settings:

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