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:
instanceShaderBindingTableRecordOffsetis set on the CPU/host side within theVkAccelerationStructureInstanceKHRdescriptor.geometryIndexis the zero-based index of the geometry description within a bottom-level acceleration structure (BLAS).sbtRecordStridedictates the spacing between consecutive geometries in the SBT.sbtRecordOffsetis provided directly by the caller inside the shader viatraceRayEXT.
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:
- Radiance/Primary Rays: Require full closest hit shaders to evaluate complex materials, textures, and lighting calculations.
- 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.
- 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,
sbtRecordOffsetis evaluated dynamically in shader code per call totraceRayEXT. - Range: The offset value must ensure that
HitGroupIndexdoes 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
sbtRecordOffsetparameter only affects hit group calculations; miss shaders are indexed entirely separately via themissIndexparameter.