How To: Implement Hybrid Frustum Traced Shadows

How To: Implement Hybrid Frustum Traced Shadows

By Jon Story, Nvidia

August 12th 2016 at 12:09PM

Nvidia senior developer technology engineer Jon Story explains the method behind the graphics specialist’s new shadow technique and reveals how devs can make use of it in their own games

Nvidia HFTS (Hybrid Frustum Traced Shadows) is an advanced hybrid shadow technique that combines frustum tracing, screen-space anti-aliasing and variable penumbra soft shadow filters.

Frustum tracing means that the primitives of occluding objects are ray-traced from the light’s point of view to form a shadow on the receiving surfaces. It is a form of ray tracing and, as such, produces perfect results.

HFTS smoothly transitions from a geometrically accurate hard shadow to a super-soft result in real time. It also addresses issues that other shadow technologies do not, like shadow detachment, aliasing and interference from overlapping blockers.

Real-time ray traced shadows have been the holy grail of graphics programmers for years, and the inclusion of HFTS in Tom Clancy’s The Division is an industry first in this regard.

HFTS brings ray tracing quality to the table without requiring any storage of primitives. In turn, this results in shadows that are a near perfect representation.

Unlike pure shadow mapping techniques, HFTS does not suffer from aliasing due to mismatch in light to screen space mapping, shadow acne due to z-bias tolerance, or peter-panning due to excessive z-bias factor.


Debut in The Division

When searching for a title to be the test case for HFTS, The Division came out on top because of its highly detailed, open-world levels. However, HFTS is suitable for most games and we’re likely to see it being used in more and more games in the future. HFTS is suited to any game that has shadows, but is best suited to sun shadows.

HFTS is only active during daylight hours in The Division. At night-time the shadowing technique is switched to percentage closer soft shadows (PCSS). There are shadows cast by the moon and many spotlights around the map, which meant that PCSS was a better choice to keep within the performance budget.

This method is actually controlled within The Division itself. The Nvidia ShadowWorks library has the ability to let you change which technique is used from frame to frame.

HFTS will run on all DirectX 11-capable hardware. The question of how fast it will run on lower-end cards will determine the choice a developer makes about how it should be enabled. In The Division, the choice was made to only enable HFTS for high-end cards that support hardware conservative raster. Unfortunately, HFTS is unlikely to run fast enough for consoles.

As ever, speed is the name of the game. We’ll be working on making it even faster for the future so that it can comprehensively support even more games.

More information on ShadowWorks can be found at developer.nvidia.com/shadowworks.


Getting started with HFTS

Here is an overview of the API calls needing to be made by a game engine to get HFTS and other shadowing techniques supported by the library to work.

It’s broken down into the most logical way in which a game engine is likely to use the library:

GameEngine::OnCreateDevice()

{

       // Check the version of the library matches the header file

       GFSDK_ShadowLib_GetVersion()

 

       // Create the shadow lib context

       GFSDK_ShadowLib_Create()

 

       // Create the fullscreen shadow buffer where the shadow result will 
be placed

       GFSDK_ShadowLib_Context::AddBuffer()

 

       // Create the light space maps used to store the intermediate results

GFSDK_ShadowLib_Context::AddMap()

}

 

GameEngine::OnResizeWindow()

{

       // Destroy and re-create new buffers based upon the resize params

       GFSDK_ShadowLib_Context::RemoveBuffer()

       GFSDK_ShadowLib_Context::AddBuffer()

       GFSDK_ShadowLib_Context::RemoveMap()

       GFSDK_ShadowLib_Context::AddMap()

}

 

GameEngine::OnRender()

{

       // Set the various rendering params (such as the technique)

       GFSDK_ShadowLib_Context::SetMapRenderParams()

 

       // Update the bounds of the light frusta to be used this frame

       GFSDK_ShadowLib_Context::UpdateMapBounds()

 

       // Clear / Initialize the resources used in light space rendering

       GFSDK_ShadowLib_Context::InitializeMapRendering()

 

       // Set rendering state for light space rendering

       GFSDK_ShadowLib_Context::BeginMapRendering()

      

       // Your standard light space draw calls go here

 

       // Restore previous rendering state

GFSDK_ShadowLib_Context::EndMapRendering()

      

       // Clear and render the final shadow buffer result

       GFSDK_ShadowLib_Context::ClearBuffer()

       GFSDK_ShadowLib_Context::RenderBuffer()

       GFSDK_ShadowLib_Context::FinalizeBuffer()

      

       // Optional - if not wanting to perform a custom modulate with 
scene color

       GFSDK_ShadowLib_Context::ModulateBuffer()      

}

 

GameEngine::OnDestroyDevice()

{

       // Destroy resources and the shadow lib context

       GFSDK_ShadowLib_Context::RemoveMap()                  

       GFSDK_ShadowLib_Context::RemoveBuffer()

       GFSDK_ShadowLib_Context::Destroy()

}


THIS MONTH’S TUTOR

Name: Jon Story

Role: Senior Developer Technology Engineer

Company: Nvidia

Bio: With more than 20 years of experience in the games and computer hardwares industries, Story is well-versed in 3D graphics, GPU/CPU optimisation and parallel programming methods