Role of sbtRecordOffset in GLSL traceRayEXT

In Vulkan ray tracing using GLSL, the sbtRecordOffset parameter in traceRayEXT controls which hit group is selected from the Shader Binding Table (SBT) when a ray intersects geometry. This article explains the function of sbtRecordOffset, details its position within the SBT addressing calculation, and illustrates how it enables the pipeline to execute different shader programs—such as radiance and shadow shaders—for the exact same geometry.

The Shader Binding Table Indexing Formula

When a ray intersects a primitive, the ray tracing pipeline calculates the index of the hit shader record to execute from the SBT. According to the Vulkan specification, the formula for determining this hit group index is:

HitGroupIndex = instanceShaderBindingTableRecordOffset 
              + sbtRecordOffset 
              + (sbtRecordStride * geometryIndex)

Within this equation:

  • instanceShaderBindingTableRecordOffset is set on the CPU/host side within the VkAccelerationStructureInstanceKHR descriptor.
  • geometryIndex is the zero-based index of the geometry description within a bottom-level acceleration structure (BLAS).
  • sbtRecordStride dictates the spacing between consecutive geometries in the SBT.
  • sbtRecordOffset is provided directly by the caller inside the shader via traceRayEXT.

Primary Function: Ray-Type Differentiation

The fundamental purpose of sbtRecordOffset is to select different behavior based on the type of ray being traced, without requiring separate geometry or multiple acceleration structures.

Typical ray tracing pipelines define multiple ray types:

  1. Radiance/Primary Rays: Require full closest hit shaders to evaluate complex materials, textures, and lighting calculations.
  2. Shadow/Occlusion Rays: Require simplified hit logic (often just an any-hit shader to determine visibility or even terminating immediately upon intersection) without evaluating full material properties.
  3. Secondary/Probe Rays: May require custom sampling routines, such as global illumination gathering.

By providing a distinct sbtRecordOffset for each ray type, you redirect the pipeline to the appropriate shader record for that specific ray context.

Practical Example

Consider a scene with two ray types:

  • Ray Type 0 (Radiance): Uses sbtRecordOffset = 0
  • Ray Type 1 (Shadow): Uses sbtRecordOffset = 1

Because each geometry has two corresponding hit records (one for radiance, one for shadow), the sbtRecordStride passed to traceRayEXT is set to 2.

When casting a radiance ray:

traceRayEXT(
    topLevelAS,     // accelerationStructureEXT
    gl_RayFlagsOpaqueEXT,
    0xFF,           // cullMask
    0,              // sbtRecordOffset (Primary Ray)
    2,              // sbtRecordStride (Number of ray types)
    0,              // missIndex
    origin,
    tMin,
    direction,
    tMax,
    0               // payload location
);

For geometry 0, the calculated index is: instanceOffset + 0 + (2 * 0) = instanceOffset + 0 (Radiance Hit Group).

When casting a shadow ray from within that radiance hit shader:

traceRayEXT(
    topLevelAS,
    gl_RayFlagsTerminateOnFirstHitEXT | gl_RayFlagsOpaqueEXT | gl_RayFlagsSkipClosestHitShaderEXT,
    0xFF,
    1,              // sbtRecordOffset (Shadow Ray)
    2,              // sbtRecordStride
    1,              // missIndex (Shadow Miss)
    shadowOrigin,
    tMin,
    shadowDir,
    lightDist,
    1               // shadow payload location
);

For geometry 0, the calculated index is: instanceOffset + 1 + (2 * 0) = instanceOffset + 1 (Shadow Hit Group).

For a secondary geometry 1 in the same BLAS:

  • The radiance ray uses index instanceOffset + 0 + (2 * 1) = instanceOffset + 2.
  • The shadow ray uses index instanceOffset + 1 + (2 * 1) = instanceOffset + 3.

Summary of Constraints and Behavior

  • Dynamic Selection: Unlike instance-level offsets, sbtRecordOffset is evaluated dynamically in shader code per call to traceRayEXT.
  • Range: The offset value must ensure that HitGroupIndex does not exceed the total number of hit records allocated in the SBT hit buffer; accessing out-of-bounds records results in undefined behavior or device loss.
  • Independence from Miss Shaders: The sbtRecordOffset parameter only affects hit group calculations; miss shaders are indexed entirely separately via the missIndex parameter.