How rayQueryGetIntersectionTEXT Works in GLSL

This article explains how the rayQueryGetIntersectionTEXT function retrieves the parametric hit distance (\(t\)) during ray query execution in GLSL using the GL_EXT_ray_query extension. It covers the underlying mathematical ray representation, how the hardware tracks intersection states, the distinction between candidate and committed hits, and a practical implementation example.

The Parametric Ray Equation and Hit Distance

Ray tracing engines represent a ray mathematically using the parametric line equation:

\[\mathbf{P}(t) = \mathbf{O} + t \cdot \mathbf{D}\]

Where:

  • \(\mathbf{O}\) is the ray origin (rayQueryGetIntersectionRayOriginEXT).
  • \(\mathbf{D}\) is the ray direction (rayQueryGetIntersectionRayDirectionEXT).
  • \(t\) is the scalar parametric distance along the ray.

When a ray intersects geometry (such as an axis-aligned bounding box or a triangle), the intersection hardware calculates the exact value of \(t\) where the intersection takes place. The function rayQueryGetIntersectionTEXT retrieves this calculated scalar \(t\) value directly from internal hardware ray query registers.

Function Signature and Parameters

The GLSL intrinsic is defined as:

float rayQueryGetIntersectionTEXT(rayQueryEXT rayQuery, bool committed);

The function takes two parameters:

  1. rayQuery: The ray query traversal object tracking state.
  2. committed: A boolean flag determining which intersection register to read from:
    • false (Candidate Intersection): Reads the hit distance of the candidate geometry currently being evaluated. This is used during traversal when processing non-opaque geometry or custom intersection shaders to evaluate whether a hit should be accepted or rejected.
    • true (Committed Intersection): Reads the hit distance of the closest accepted intersection confirmed so far. Once traversal completes, this returns the final, confirmed hit distance along the ray.

Internal Traversal Mechanism

  1. Initialization: The ray is launched with rayQueryInitializeEXT, establishing \(t_{\min}\) and \(t_{\max}\) bounds.
  2. Traversal Steps: During rayQueryProceedEXT, the ray traversal unit tests nodes in the acceleration structure. When a primitive falls within current \(t\) bounds:
    • The candidate hit distance is updated in hardware scratch memory.
    • If non-opaque, execution yields back to the shader. Calling rayQueryGetIntersectionTEXT(rayQuery, false) returns this candidate \(t\) value.
  3. Commitment: If the hit is confirmed (either automatically for opaque primitives or via rayQueryConfirmIntersectionEXT), the candidate \(t\) becomes the new active \(t_{\max}\) limit for subsequent tests, and the committed register is updated.
  4. Final Result: After rayQueryProceedEXT returns false, rayQueryGetIntersectionTEXT(rayQuery, true) retrieves the scalar \(t\) of the closest confirmed hit.

Practical Implementation

The following example demonstrates how to extract the hit distance after traversing an acceleration structure:

#version 460
#extension GL_EXT_ray_query : require

layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS;

void main() {
    rayQueryEXT rayQuery;
    vec3 origin = vec3(0.0, 1.0, 0.0);
    vec3 direction = vec3(0.0, 0.0, 1.0);
    float tMin = 0.001;
    float tMax = 1000.0;

    // Initialize the query
    rayQueryInitializeEXT(
        rayQuery, 
        topLevelAS, 
        gl_RayFlagsOpaqueEXT, 
        0xFF, 
        origin, 
        tMin, 
        direction, 
        tMax
    );

    // Process all intersections
    while (rayQueryProceedEXT(rayQuery)) {
        // For candidate intersections (if handling non-opaque geometry)
        float candidateT = rayQueryGetIntersectionTEXT(rayQuery, false);
    }

    // Check if an intersection was committed
    if (rayQueryGetIntersectionTypeEXT(rayQuery, true) != gl_RayQueryCommittedIntersectionNoneEXT) {
        // Retrieve the final parametric hit distance
        float hitT = rayQueryGetIntersectionTEXT(rayQuery, true);
        vec3 hitPosition = origin + hitT * direction;
    }
}

Physical Distance vs. Parametric \(t\)

The scalar returned by rayQueryGetIntersectionTEXT represents world-space Euclidean distance only if the ray direction vector (\(\mathbf{D}\)) supplied to rayQueryInitializeEXT is normalized.

If the direction vector has a length other than \(1.0\), the actual physical distance to the hit point must be computed as:

\[\text{Distance} = t \cdot \|\mathbf{D}\|\]

Normalizing the direction vector prior to ray initialization ensures that rayQueryGetIntersectionTEXT directly reflects true world-space units.