Shadow Tracing with AMD Technologies: How Shadows Are Implemented in The Riftbreaker

The Riftbreaker is an isometric strategy game that combines elements of survival, exploration and hack'n'slash. Powered by Exor Studios' proprietary Schmetterling Engine 2.0, The Riftbreaker leverages the latest developments in the gaming industry, one of which is real-time ray tracing. In this article, we will talk about what problems we faced in the studio when implementing ray tracing in the game and what solutions we found for them.



image


Dynamically changing time of day, varied weather effects, and many explorable biomes make real-time ray tracing a great choice for The Riftbreaker





A quick tour of ray tracing



The world of The Riftbreaker is completely dynamic and destructible. Almost all objects that are present in the environment can be influenced by the player. Vegetation can be bent, burned, and dissolved. Thousands of creatures can swarm around the player and completely fill the screen. This kind of gameplay requires a special approach to rendering fully dynamic shadows.



Previously, to generate shadows in the Schmetterling 2.0 engine, they used the real-time shadow map method (without pre-computed shadow maps themselves). This solution was the most optimal due to the fully dynamic geometry of the scene. However, it was not possible to use any pre-computed lightmaps, since they did not fit this very geometry. That is why dynamic shadow maps were the only real solution for a long time. Although they are widely used throughout the industry, they have a number of limitations.



image


Fragment of the boss battle. Attached to the boss is a shadow-casting point light that adds visual fidelity to the scene.



The latest generations of GPUs are powerful enough to perform real-time ray tracing calculations. And the advent of new graphics cards has allowed the studio to finally introduce ray-traced shadows that deliver superior results over traditional shadow mapping methods.



The basic principle of shadow tracing is that we do not look at the scene from the point of view of the light source and do not look for all kinds of shadow castes, as is done in shadow maps. Ray tracing allows you to simply launch rays into a light source. If the beam hits an obstacle, then there will be no direct illumination from it. If it reaches the light source, no shadows are added. This algorithm is quite simple, but it gives excellent results and offers solutions to common shadow rendering problems. However, it is very picky about GPU performance.



image


Every element of the scene's dynamic geometry can cast a shadow. The challenge was to make these shadows appear as accurate as possible while maintaining performance.



Adding a completely new rendering technology to your own game engine is not an easy task. In the case of The Riftbreaker, the studio's collaboration with AMD helped a lot. They provided a proprietary GPUOpen RT Shadows library containing both ray tracing and noise reduction solutions to clean up the ray tracing pass results. However, before using this library, I had to develop a DirectX 12 renderer for the game engine. The reason for this was the DirectX Raytracing API (aka DXR) introduced in the DirectX 12 Ultimate standard. This API enables the new shaders and hardware ray tracing capabilities of modern GPUs.



image


The Riftbreaker . .



Another advantage of working with AMD is the open source code of their solutions. This enables technology to be implemented that is compatible with the latest gaming platforms on the market, including next generation consoles. It's also worth noting that platform compatibility influenced the selection and rendering API. Two options were considered: Vulkan and DirectX 12. Although Vulkan includes ray tracing, this API is not available on Xbox or PlayStation, and as of this writing, only Nvidia supports it on PC. Moving to DirectX 12 brings native ray tracing support to Xbox and Windows PCs, allowing you to use hardware from any manufacturer.



image


As weather conditions change, partial shade also changes. In this example, you can see how the shadows become softer when it rains and sharper as the sun shines.



The benefits of implementing ray-traced shadows can vary depending on the implementation scenario. In the case of The Riftbreaker, the most important features are:



  • "Infinite" shadow resolution: the quality of the shadow does not depend on the distance from the subject to the camera, unlike traditional shadow mapping methods. Each pixel on the screen has individually calculated shading, resulting in more accurate and stable shadows without flickering artifacts.
  • shifting penumbra: Ray-traced shadows dynamically simulate situations such as the transition from a cloudy sky in rain to bright midday.
  • low cost of calculating additional light sources that cast shadows. With the current implementation of ray-traced shadows in The Riftbreaker, you can compute up to 4 shadow-casting lights simultaneously without significant performance degradation. The cost of adding additional lights in the case of shadow maps is much higher.


All of these benefits come at the expense of significant performance loss. Even with the latest GPUs that support hardware-accelerated ray tracing, FPS values ​​with all ray tracing effects enabled can be up to half as much when comparing the same scene without ray tracing.





Implementing ray-traced shadows



Adding ray-traced shadows to a scene is a complex process that results in an extremely detailed map of lighted and unlit pixels. During the first pass of ray tracing, it is necessary to restore the position of all pixels in the visible area of ​​the screen and direct rays from these pixels to all light sources that affect them. These coordinates are taken from the depth buffer. If the beam reaches the light source, then the surface is directly illuminated. If the beam hits an obstacle in its path, the surface will be shaded. In addition, to optimize the process, all surface rays are cast with normal maps facing away from the light source. The next pass defines the type of shader that will be applied to the intersection of the ray and surface. DXR API uses shader for processing,based on the result of the ray tracing, and checks it against the existing shader table.



image


Example of a game scene with ray tracing - soft shadows and ambient occlusion enabled The



Radeon RX 6000 series graphics cards used to develop ray tracing in The Riftbreaker are powerful and capable of launching millions of rays per second. However, even more information is needed to get an accurate idea of ​​how light will behave in the game world. In the case of offline rendering, this usually means casting thousands of rays in all directions for every pixel. No modern hardware is capable of performing these calculations in real time, let alone doing it 60 times per second. So, you need to somehow create an accurate shadow map using incomplete data.



image


. , .



Lack of accurate data creates many problems and can lead to degraded visual quality of the scene. While we will have most of the information needed to render objects, details such as paths, edges, and soft shadows will blur and blend into each other. A limited number of beams per frame results in noisy shadows. Then another open source GPU library comes into play - AMD's FidelityFX Denoiser. Removing noise is a complex process that has become possible thanks to the widespread use of temporal methods that analyze past frames and combine them into a new one. AMD Noise Reduction lets you quickly determine the average of the available data and what properties to apply to a given pixel to get a crisp image without any visible compromise.



image


The same scene after removing noise



image


, . ​​ .







Of course, the implementation of ray-traced shadows turned out to be not such an easy task to limit ourselves to using a couple of ready-made libraries. The Riftbreaker presents its own unique challenges requiring specific solutions.



The first such problem was the huge number of dynamic objects present in the game world. In The Riftbreaker, you take on the role of a scientist exploring an exoplanet inhabited by numerous alien species of flora and fauna. The player is now and then attacked by hordes of thousands of alien creatures. Each of them must cast its own shadow. Combined with a dynamic vegetation system that responds to wind, shock waves and bending forces applied by other objects, this has become a major optimization problem.



image


Thousands of entities interact with each other in real time.



The main issue was caused by the way the upper acceleration framework for ray tracing processes the data. This structure stores information about objects in the scene that is used during the ray tracing pass. This data can only be stored as a pre-baked lower-level acceleration structure that contains information about the object's vertices. This is not a problem when it comes to rocks or buildings, but all units with skeletal, dynamically blended animations, and dynamic vegetation are completely out of step with the pre-baked data assumption.



image


«» . , . : , .



To provide the acceleration framework with all the necessary data, some serious steps had to be taken. Each dynamic object in the scene is individually baked into a completely new static model that can be processed during the ray tracing pass. To maintain accuracy, this process must be repeated every frame. The complexity of this task is added by the fact that The Riftbreaker has a dynamic weather system. Because of this, it is possible that the light source is at an angle, due to which the object casts a shadow into the frame, without being directly visible. This means that during the baking process, you need to take into account not only objects in the visible part of the screen, but also outside it.



image


An image of the entire acceleration structure was prepared in just one render frame



The process of preparing acceleration structures is very cumbersome in terms of CPU computations and can easily become a bottleneck for the entire renderer. The Schmetterling Engine 2.0 reduces the load on the central processing unit by intensively parallelizing all the processes required to prepare a scene for ray tracing. By distributing the operations between the CPU and GPU, it was possible to find the processing power needed to perform all of these operations in every frame. In the case of a test scenario that includes about 6,000 creatures attacking the player's base, it was possible to reduce the rendering time from 60 ms to 17 ms by parallelizing CPU-dependent tasks (using an AMD Ryzen 9 3900X CPU and a pre-release version of the Radeon 6800XT).



image


The Riftbreaker. , , , .





-



Another unique challenge that engineers had to solve when implementing ray tracing techniques in The Riftbreaker turned out to be related to the vegetation system, albeit in a slightly different way. The textures used by foliage are usually alpha tested and often have transparent areas. However, for raikast, this does not matter. As soon as the ray hits a transparent pixel on the texture, it returns the normal "hit". This does not necessarily mean that the pixel from which we are producing the rays must be covered with shadow, because if the texel we hit is transparent, the ray must continue to move towards the light source. There are many such textures in the game, so it was necessary to find a solution to this problem and add support for alpha testing in the AnyHit shader.



image


- . , .



The studio's approach was to use a solution used in traditional rendering techniques. When hitting the surface, we get the barycentric coordinates of the triangle at the point of intersection. These coordinates are not enough to determine which pixel of the texture the ray hit, and you can only find out the position of the intersection point in the triangle. However, at this stage, we can determine which peaks should be considered. Each vertex of the triangle has a set of UVW coordinates assigned to it by the graphic designer during texturing. Knowing which triangle we fell into, the coordinates of the intersection inside this triangle and which part of the texture should cover it, we can perform an alpha test.



image


The polygons underneath the vegetation textures are opaque to the rays. I had to introduce an additional method of checking whether they fall into the place on the texture that was really opaque.



However, before all of the above happens, the acceleration structure needs to pass information about textures and their positions. For this, we need a shader table, which is essentially a data bank for the GPU. It lists the textures used in the scene and the values ​​assigned to them in the index and vertex buffers. With the shader table, you can quickly get all the model and texture data you need to complete the subsequent shading steps.



image


As The Riftbreaker takes place in an uninhabited alien world, there are many wild animals to be found here



If the result is “opaque,” ​​the shading data is applied and the process ends for that particular ray. In case of opaque hits, an alpha test is performed. As already mentioned, when a ray intersects with a surface, from here you can get the barycentric coordinates of the point in the triangle with which it collided. Knowing these coordinates and referring to the index buffer, you can get the indices of the vertices of the specified triangle. This retrieves the UVW coordinates. With the data provided by the index buffer, you can now find information in the vertex buffer about where these vertices fall on the texture. Only after all these actions do we get an answer to the question whether the ray collided with an opaque or transparent surface. If the alpha value at the intersection point is below the transparency threshold, the ray continues to intersect the scene.



image


This way we get the exact result in every frame. Click image for a larger view.



Alpha testing increases the material cost in terms of ray-traced shadow computation over traditional methods, so it is best to avoid them where possible. In a jungle scenario, the cost of sending such a beam is about 20% higher than that of a beam hitting an opaque object. To reduce the number of rays hitting transparent surfaces, optimizations are needed by limiting the surface area of ​​all transparent objects. The camera view in The Riftbreaker is isometric, so the number of polygons visible at the same time is naturally limited, and we can easily increase the polygon rendering time of most objects without affecting the GPU performance.





Conclusion



The Riftbreaker's use of ray tracing has only benefited. The world of the game has become more believable, which contributes to better immersion. The small details added with ray tracing definitely help improve perception. Seeing a bright comet in the sky casting shadows on dynamic objects on earth is a truly beautiful sight. Cloudy days, in turn, greet the player with soft shadows and a more muted color palette. The Riftbreaker will surely take its well-deserved place in the lineup of next generation games.



image


The Riftbreaker is coming to PC and consoles in 2021.



All Articles