GLSL Ray Queries: Validating Procedural Hits
GLSL ray queries enable shaders to traverse acceleration structures
inline, halting at candidate intersections to allow user code to accept,
reject, or compute hit results. When traversing procedural geometry
defined by Axis-Aligned Bounding Boxes (AABBs), ray queries pause to let
the developer determine if a mathematical primitive inside the bounding
volume was actually struck. This article explains the procedural
candidate hit validation lifecycle in GL_EXT_ray_query,
detailing the operational boundary between custom intersection logic,
parametric hit generation, and the confirmation routines that commit
intersections to the ray traversal state.
Ray Query Traversal and Candidate Discovery
During an inline ray query traversal loop driven by
rayQueryProceedEXT(), the traversal stops whenever the
hardware traversal unit encounters an intersection that cannot be
automatically resolved. For procedural geometry, the acceleration
structure stores bounding boxes rather than explicit polygons.
When a ray intersects an AABB, rayQueryProceedEXT()
returns true, and calling
rayQueryGetIntersectionTypeEXT(rayQuery, false) yields
gl_RayQueryCandidateIntersectionAABBEXT. This flags that a
procedural primitive's bounding volume has been entered, temporarily
suspending traversal so that the shader can execute analytical
intersection math.
Procedural Generation vs. Triangle Confirmation
A critical architectural distinction in GL_EXT_ray_query
is how triangle and procedural candidates are confirmed:
- Triangle Candidates
(
rayQueryConfirmIntersectionEXT): Built-in fixed-function intersection units automatically calculate triangle barycentrics and the intersection distance \(t\). The shader only needs to decide whether to accept or ignore the hit (for example, evaluating alpha masking) usingrayQueryConfirmIntersectionEXT(rayQuery). - Procedural Candidates
(
rayQueryGenerateIntersectionEXT): Because an AABB only represents a bounding volume, no hit point or parametric distance \(t\) exists until the shader calculates it. Therefore, procedural hits are not confirmed via the parameterlessrayQueryConfirmIntersectionEXT(). Instead, they are validated and submitted usingrayQueryGenerateIntersectionEXT(rayQuery, float tHit).
Attempting to execute rayQueryConfirmIntersectionEXT()
on a candidate of type
gl_RayQueryCandidateIntersectionAABBEXT is invalid
according to the SPIR-V and GLSL specifications; the procedural
validation path strictly requires generating an explicit distance \(t\).
The Procedural Hit Validation Workflow
Validating a procedural candidate within a GLSL ray query involves a sequential execution flow:
Candidate Identification: After
rayQueryProceedEXT()returnstrue, verify that the candidate is procedural:if (rayQueryGetIntersectionTypeEXT(rayQuery, false) == gl_RayQueryCandidateIntersectionAABBEXT) { // Proceed with custom procedural intersection }Ray Space Transformation: Retrieve the object-space ray parameters for the candidate primitive using built-in query accessors such as
rayQueryGetIntersectionObjectRayOriginEXT(rayQuery, false)andrayQueryGetIntersectionObjectRayDirectionEXT(rayQuery, false).Analytical Intersection: Execute user-defined intersection equations (such as ray-sphere, ray-cylinder, or signed distance field raymarching) within the local bounding space. This test yields a candidate hit distance \(t\).
Range Testing and Submission: Verify that the computed \(t\) satisfies the current ray bounds (\(t_{\text{min}} \le t \le t_{\text{max}}\)). If the hit is valid, invoke:
rayQueryGenerateIntersectionEXT(rayQuery, tHit);
State Commitment and Traversal Shortening
Executing rayQueryGenerateIntersectionEXT() validates
the procedural hit by communicating the calculated distance back to the
traversal engine. If \(tHit\) is closer
than the current committed intersection distance and lies within the
active \([t_{\text{min}},
t_{\text{max}}]\) interval, the query replaces the current
candidate state with this hit.
Upon the next invocation of rayQueryProceedEXT(), the
traversal engine commits this intersection, effectively clamping \(t_{\text{max}}\) to the newly validated
distance. This dynamic ray shortening prunes subsequent, more distant
bounding volumes from triggering false procedural candidates throughout
the remainder of the traversal.