Greetings from the Graphics team!
We are glad to announce that as of the 2023.2 tech stream, the Unity Ray Tracing API has been extended to provide Inline Ray Tracing support across all shader stages, when targeting capable Windows and Console platforms.
Before sharing information on how to enable Inline Ray Tracing support for your shaders, along with platform support and code examples, let’s quickly review the concepts of the ray tracing pipeline and inline ray tracing.
Ray Tracing Pipeline
The High Definition Rendering Pipeline’s various Ray Tracing effects, such as shadows and reflections, are all implemented via the Unity Ray Tracing API and using the traditional “ray tracing pipeline”.
The ray tracing pipeline works by creating and building a Ray Tracing Acceleration Structure representing the scene’s geometry, to be traversed on the GPU using the Ray Generation shader. In cases where a ray is calculated to intersect with the bound acceleration structure, the appropriate “hit shader” is then invoked in order to perform the required shading. Alternatively, if geometry is not intersected by the ray generation shader at all, a “miss shader” may be invoked in order to perform background / environment shading.
To enable this, a dynamic shader table is created by the ray tracing pipeline, which defines and binds the various possible “hit” and “miss” shader programs to be potentially invoked by the ray generation shader. While this system is ideal for the ray tracing of complex pipelines and environments with high shader complexity, as in the case of the high-definition render pipeline, it does not permit the utilization of hardware accelerated ray tracing in general-purpose compute programs. Furthermore, dynamic ray tracing shader tables may introduce unnecessary complexity and overhead for simpler scenarios and use cases.
Inline Ray Tracing
DXR1.1 expands upon the DX12 Ray Tracing API by introducing the ability to issue inline ray queries directly within compute and rasterization shader stages. This provides developers with an alternative method of performing hardware accelerated ray tracing, and unlocks new and exciting possibilities for shader and custom pipeline effect authoring.
As of Unity 2023.2, this new functionality is now fully supported by the Ray Tracing API across all shader stages, when targeting DXR1.1 capable Windows Platforms, Xbox Series and Playstation 5.
Shader Support
The RayQuery object is defined in HLSL when using the DirectX Shader Compiler (DXC) whereas other shader compilers used by different platforms either don’t define the RayQuery object, or its name is different. Because of this, the recommended approach is to include the UnityRayQuery.cginc header and use the UnityRayQuery object instead of RayQuery:
#include <UnityRayQuery.cginc>
Enabling Inline Ray Tracing (RayQuery) support in shader code can be done using one of the 2 options:
#pragma require inlineraytracing
or
#pragma multi_compile _ UNITY_DEVICE_SUPPORTS_INLINE_RAY_TRACING
Declaring either pragma will force the shader to compile using the DXC (DirectX Shader Compiler), and will require Shader Model 6.5+ and Inline Ray Tracing support on the target device.
The latter approach (multi_compile) will generate and compile an additional inline ray tracing shader variant, to be loaded at runtime when targeting platforms with ray queries support. This allows you to define and maintain two (or more) variants for your shader - one that utilizes hardware ray queries, and one that relies on more traditional methods:
#include <UnityRayQuery.cginc>
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _ UNITY_DEVICE_SUPPORTS_INLINE_RAY_TRACING
#if UNITY_DEVICE_SUPPORTS_INLINE_RAY_TRACING
RaytracingAccelerationStructure g_AccelStruct;
#endif
float4 vert() : SV_POSITION { return float4(0, 0, 0, 1); }
float4 frag() : SV_Target
{
float4 ret = float4(0, 0, 0, 0);
#if UNITY_DEVICE_SUPPORTS_INLINE_RAY_TRACING
// Cast shadow rays using inline ray tracing
UnityRayQuery<RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH> query;
#else
// Sample a shadow map
#endif
return ret;
}
Please check DirectX specification for DXR 1.1 and RayQuery object for more information on using ray queries in shaders: https://microsoft.github.io/DirectX-Specs/d3d/Raytracing.html#rayquery
In C# you can use SystemInfo.supportsInlineRayTracing to check if the system supports Inline Ray Tracing in shader code. This check can be done before dispatching a compute shader that uses Inline Ray Tracing for example.
Once a Ray Tracing Acceleration Structure is created and built, you are required to bind the RTAS to a specific shader object. This can be done indirectly using the CommandBuffer API:
- CommandBuffer.SetRayTracingAccelerationStructure
- CommandBuffer.SetGlobalRayTracingAccelerationStructure
As well as directly using the Shader API:
Soft Shadows Example
The following example demonstrates the use of Inline Ray Tracing in compute shaders, in order to implement a custom shadow casting pass. Offsetting the ray direction enables a varying penumbra to control the softness of ray traced shadows:
Custom shadow pass with variable penumbra, implemented using Inline Ray Tracing
RayDesc shadowRay;
shadowRay.Origin = worldSpacePos + worldNormal * 0.01f;
shadowRay.TMin = 0;
shadowRay.TMax = 1e20f;
UnityRayQuery<RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | RAY_FLAG_CULL_BACK_FACING_TRIANGLES> shadowQuery;
const uint sampleCount = g_TemporalAccumulationStep < 10 ? 8 : 2;
// A value of 0 means that the pixel is not in shadow.
float shadow = 0;
for (uint i = 0; i < sampleCount; i++)
{
float3 localDir = normalize(float3(g_ShadowSpread, g_ShadowSpread, 1) * float3(2 * RandomFloat01(rngState) - 1, 2 * RandomFloat01(rngState) - 1, 1));
shadowRay.Direction = mul(g_LightMatrix, localDir);
// Assume everything is opaque. Cutout materials are not supported when using inline ray tracing.
shadowQuery.TraceRayInline(g_AccelStruct, RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | RAY_FLAG_CULL_BACK_FACING_TRIANGLES, 0xff, shadowRay);
shadowQuery.Proceed();
shadow += shadowQuery.CommittedStatus() == COMMITTED_TRIANGLE_HIT;
}
float prevShadow = g_Output[id.xy];
// Accumulate new samples into the previous frame's shadow map.
float result = lerp(prevShadow, 1 - (shadow / sampleCount), 1 / float(g_TemporalAccumulationStep + 1));
g_Output[id.xy] = result;
You can access the full sample project here: https://github.com/INedelcu/InlineRayTracingShadows
Reach out
Please give the new Inline Ray Tracing support a try and let us know what you think. We are also inviting you to share any interesting implementations and effects you may come up with when experimenting with ray queries.
Your feedback has been instrumental so far as it helps us prioritize the most meaningful solutions. Please check out our public roadmap to vote on the features that best suit your needs. If there are additional changes you’d like to see, feel free to submit a feature request, or contact the team directly in this thread or the graphics forum.