At the Games Developers Conference CD Projekt RED Engine Programmer Przemysław Czatrowski held a panel titled “Solving Visibility and Streaming in the The Witcher 3: Wild Hunt with Umbra 3” explaining how the developer used the Umbra 3 middleware to help building the world, and its advanced ambient occlusion culling technology to render only the objects that are actually visible on the screen, massively reducing the load on the video card.
Here’s how the occlusion process works:
- The world is split in tiles, and for each tile a “tome” data package is built, including the occlusion data. The engine determines which tomes are needed to be shown depending on the camera position and direction.
- If the newly determined set of tomes differs from the previous set, an asynchronous compitation process starts, creating a new tome collection, that is sent to the renderer to replace the one previously rendered.
- Tomes that are not used anymore are removed from the stream to free up memory.
The following screenshots showcase the process in sequence in the Skellige area. The green portion is the one currently displayed. As the camera moves the yellow area is determined to be included within the scene, and the new tome collection is built, adding up its objects to the current one.
In the video below you can see the same process in motion in the game’s editor.
Quite a few in-engine tools were developed for the artist to use with this technology:
- A detachable rendering camera that can be split from the occlusion camera.
- The streaming visualization feature shown in the video above.
- Memory and geometry statistics visualization
Here are a sample scene from the city of Novigrad comparing the actual screenshot in game with what is rendered by the engine and what isn’t thanks to the occlusion technology. You can see the camera displayed in yellow.
In this scene 43 tomes are loaded for 61 megabytes of data. An additional 15 mb of inter-tome data is generated on the fly. There are over 62,000 static mesh chunks in the whole scene, but less than 500 are visible and rendered (about 0.76% of the total). It takes only 1.8 ms (query time) to produce those results. The process doesn’t just affect static meshes but also particles, skinned meshes, simulated meshes and decals (blood, burns and so forth).
In the gallery below you can enjoy more examples. Unfortunately they are much lower in resolution, but still nice to see.
We also learn that Skellige is 8×8 km in size, there are between 45,000 and 50,000 static mesh chunks loaded at any given time and the average amount of chunks actually rendered is between 1% and 5%.
The city of Novigrad is 8.5×8.5 km, there are over 100,000 mesh chunks loaded at all times, and the average amount of those visible and rendered varies between 2% and 5%, but there are particular situations where there’s less than 1% loaded like above.
The size of Umbra data stored in the hard drive (tome data plus additional data) is 300 megabytes for Skellige and 250 for Novigrad. During runtime Skellige has 40-80 megabytes loaded in memory at any given time, while Novigrad has between 45 and 80.
The chunks mentioned above are parts of meshes. Basically a chunk is a piece of an object and occlusion is done per-chunk. Below you can see a tower with the chunks selected. For instance if only part of the roof is visible, the renderer will only display the chunk with the roof in it.
All meshes are created by hand, and the chunks normally include elements made with the same material, also set by hand.
All the meshes have LOD levels varying on distance (differently detailed models with less details for farther visualization). Each mesh has different LOD levels, each of which has different chunks to which a LOD distance is assigned, and each one is given an unique ID.
During runtime Umbra finds the right chunks and pushes them to the renderer, but only the visible ones with the right LOD level depending on the distance from the camera are rendered.
Below you can see the whole process in action in engine:
In the Q&A at the end of the presentation it was also mentioned that CD Projekt is working on implementing occlusion culling on shadows (which isn’t done in the video above) as well. Umbra can be provided with data on the direction of the light and during the query process it can calculate which chunks cast shadows that are visible from the camera, and render them alongside the objects.
Below you can enjoy a cpuple more work-in-progress screenshots included in the presentation. Remember that due to their nature, they aren’t fully representative of the final game.